<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/pysim/+/23175">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 a new pySim-shell program<br><br>pySim-prog was nice when there were only 5 parameters on a SIM that we<br>could program, and where the use case was pretty limited.  Today, we<br>have SIM/USIM/ISIM cards with hundreds of files and even more parameters<br>to program.  We cannot add a command line argument for each file to<br>pySim-prog.<br><br>Instead, this introduces an interactive command-line shell / REPL,<br>in which one can navigate the file system of the card, read and update<br>files both in raw format and in decoded/parsed format.<br><br>The idea is primarily inspired by Henryk Ploatz' venerable<br>cyberflex-shell, but implemented on a more modern basis using<br>the cmd2 python module.<br><br>See https://lists.osmocom.org/pipermail/simtrace/2021-January/000860.html<br>and https://lists.osmocom.org/pipermail/simtrace/2021-February/000864.html<br>for some related background.<br><br>Most code by Harald Welte. Some bug fixes by Philipp Maier<br>have been squashed.<br><br>Change-Id: Iad117596e922223bdc1e5b956f84844b7c577e02<br>Related: OS#4963<br>---<br>M contrib/jenkins.sh<br>A pySim-shell.py<br>M pySim/exceptions.py<br>A pySim/filesystem.py<br>A pySim/ts_102_221.py<br>M pySim/ts_31_102.py<br>M pySim/ts_31_103.py<br>M pySim/ts_51_011.py<br>8 files changed, 1,800 insertions(+), 2 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh</span><br><span>index 5ba2c8d..bfbf4e0 100755</span><br><span>--- a/contrib/jenkins.sh</span><br><span>+++ b/contrib/jenkins.sh</span><br><span>@@ -13,6 +13,7 @@</span><br><span> . venv/bin/activate</span><br><span> pip install pytlv</span><br><span> pip install pyyaml</span><br><span style="color: hsl(120, 100%, 40%);">+pip install cmd2</span><br><span> </span><br><span> cd pysim-testdata</span><br><span> ../tests/pysim-test.sh</span><br><span>diff --git a/pySim-shell.py b/pySim-shell.py</span><br><span>new file mode 100755</span><br><span>index 0000000..ce9630a</span><br><span>--- /dev/null</span><br><span>+++ b/pySim-shell.py</span><br><span>@@ -0,0 +1,213 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python3</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# Interactive shell for working with SIM / UICC / USIM / ISIM cards</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# (C) 2021 by 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%);">+from typing import List</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import json</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import cmd2</span><br><span style="color: hsl(120, 100%, 40%);">+from cmd2 import style, fg, bg</span><br><span style="color: hsl(120, 100%, 40%);">+from cmd2 import CommandSet, with_default_category, with_argparser</span><br><span style="color: hsl(120, 100%, 40%);">+import argparse</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import os</span><br><span style="color: hsl(120, 100%, 40%);">+import sys</span><br><span style="color: hsl(120, 100%, 40%);">+from optparse import OptionParser</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_51_011 import EF, DF, EF_SST_map, EF_AD_mode_map</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_31_102 import EF_UST_map, EF_USIM_ADF_map</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_31_103 import EF_IST_map, EF_ISIM_ADF_map</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.exceptions import *</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.commands import SimCardCommands</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.cards import card_detect, Card</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.utils import h2b, swap_nibbles, rpad, h2s</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.utils import dec_st, init_reader, sanitize_pin_adm</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.card_handler import card_handler</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.filesystem import CardMF, RuntimeState</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_51_011 import CardProfileSIM, DF_TELECOM, DF_GSM</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_102_221 import CardProfileUICC</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_31_102 import ADF_USIM</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_31_103 import ADF_ISIM</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class PysimApp(cmd2.Cmd):</span><br><span style="color: hsl(120, 100%, 40%);">+      CUSTOM_CATEGORY = 'pySim Commands'</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, card, rs):</span><br><span style="color: hsl(120, 100%, 40%);">+         basic_commands = [Iso7816Commands(), UsimCommands()]</span><br><span style="color: hsl(120, 100%, 40%);">+          super().__init__(persistent_history_file='~/.pysim_shell_history', allow_cli_args=False,</span><br><span style="color: hsl(120, 100%, 40%);">+                              use_ipython=True, auto_load_commands=False, command_sets=basic_commands)</span><br><span style="color: hsl(120, 100%, 40%);">+              self.intro = style('Welcome to pySim-shell!', fg=fg.red)</span><br><span style="color: hsl(120, 100%, 40%);">+              self.default_category = 'pySim-shell built-in commands'</span><br><span style="color: hsl(120, 100%, 40%);">+               self.card = card</span><br><span style="color: hsl(120, 100%, 40%);">+              self.rs = rs</span><br><span style="color: hsl(120, 100%, 40%);">+          self.py_locals = { 'card': self.card, 'rs' : self.rs }</span><br><span style="color: hsl(120, 100%, 40%);">+                self.card.read_aids()</span><br><span style="color: hsl(120, 100%, 40%);">+         self.poutput('AIDs on card: %s' % (self.card._aids))</span><br><span style="color: hsl(120, 100%, 40%);">+          self.numeric_path = False</span><br><span style="color: hsl(120, 100%, 40%);">+             self.add_settable(cmd2.Settable('numeric_path', bool, 'Print File IDs instead of names',</span><br><span style="color: hsl(120, 100%, 40%);">+                                                onchange_cb=self._onchange_numeric_path))</span><br><span style="color: hsl(120, 100%, 40%);">+           self.update_prompt()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def _onchange_numeric_path(self, param_name, old, new):</span><br><span style="color: hsl(120, 100%, 40%);">+               self.update_prompt()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def update_prompt(self):</span><br><span style="color: hsl(120, 100%, 40%);">+              path_list = self.rs.selected_file.fully_qualified_path(not self.numeric_path)</span><br><span style="color: hsl(120, 100%, 40%);">+         self.prompt = 'pySIM-shell (%s)> ' % ('/'.join(path_list))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       @cmd2.with_category(CUSTOM_CATEGORY)</span><br><span style="color: hsl(120, 100%, 40%);">+  def do_intro(self, _):</span><br><span style="color: hsl(120, 100%, 40%);">+                """Display the intro banner"""</span><br><span style="color: hsl(120, 100%, 40%);">+          self.poutput(self.intro)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @cmd2.with_category(CUSTOM_CATEGORY)</span><br><span style="color: hsl(120, 100%, 40%);">+  def do_verify_adm(self, arg):</span><br><span style="color: hsl(120, 100%, 40%);">+         """VERIFY the ADM1 PIN"""</span><br><span style="color: hsl(120, 100%, 40%);">+               pin_adm = sanitize_pin_adm(arg)</span><br><span style="color: hsl(120, 100%, 40%);">+               self.card.verify_adm(h2b(pin_adm))</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%);">+@with_default_category('ISO7816 Commands')</span><br><span style="color: hsl(120, 100%, 40%);">+class Iso7816Commands(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_select(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """SELECT a File (ADF/DF/EF)"""</span><br><span style="color: hsl(120, 100%, 40%);">+         path = opts.arg_list[0]</span><br><span style="color: hsl(120, 100%, 40%);">+               fcp_dec = self._cmd.rs.select(path, self._cmd)</span><br><span style="color: hsl(120, 100%, 40%);">+                self._cmd.update_prompt()</span><br><span style="color: hsl(120, 100%, 40%);">+             self._cmd.poutput(json.dumps(fcp_dec, indent=4))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def complete_select(self, text, line, begidx, endidx) -> List[str]:</span><br><span style="color: hsl(120, 100%, 40%);">+                """Command Line tab completion for SELECT"""</span><br><span style="color: hsl(120, 100%, 40%);">+            index_dict = { 1: self._cmd.rs.selected_file.get_selectable_names() }</span><br><span style="color: hsl(120, 100%, 40%);">+         return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    verify_chv_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+ verify_chv_parser.add_argument('--chv-nr', type=int, default=1, help='CHV Number')</span><br><span style="color: hsl(120, 100%, 40%);">+    verify_chv_parser.add_argument('code', help='CODE/PIN/PUK')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ @cmd2.with_argparser(verify_chv_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+       def do_verify_chv(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+                """Verify (authenticate) using specified CHV (PIN)"""</span><br><span style="color: hsl(120, 100%, 40%);">+           (data, sw) = self._cmd.card._scc.verify_chv(opts.chv_nr, opts.code)</span><br><span style="color: hsl(120, 100%, 40%);">+           self._cmd.poutput(data)</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%);">+@with_default_category('USIM Commands')</span><br><span style="color: hsl(120, 100%, 40%);">+class UsimCommands(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_read_ust(self, _):</span><br><span style="color: hsl(120, 100%, 40%);">+             """Read + Display the EF.UST"""</span><br><span style="color: hsl(120, 100%, 40%);">+         self._cmd.card.select_adf_by_aid(adf="usim")</span><br><span style="color: hsl(120, 100%, 40%);">+                (res, sw) = self._cmd.card.read_ust()</span><br><span style="color: hsl(120, 100%, 40%);">+         self._cmd.poutput(res[0])</span><br><span style="color: hsl(120, 100%, 40%);">+             self._cmd.poutput(res[1])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   def do_read_ehplmn(self, _):</span><br><span style="color: hsl(120, 100%, 40%);">+          """Read EF.EHPLMN"""</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.card.select_adf_by_aid(adf="usim")</span><br><span style="color: hsl(120, 100%, 40%);">+                (res, sw) = self._cmd.card.read_ehplmn()</span><br><span style="color: hsl(120, 100%, 40%);">+              self._cmd.poutput(res)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def parse_options():</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      parser = OptionParser(usage="usage: %prog [options]")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     parser.add_option("-d", "--device", dest="device", metavar="DEV",</span><br><span style="color: hsl(120, 100%, 40%);">+                     help="Serial Device for SIM access [default: %default]",</span><br><span style="color: hsl(120, 100%, 40%);">+                    default="/dev/ttyUSB0",</span><br><span style="color: hsl(120, 100%, 40%);">+             )</span><br><span style="color: hsl(120, 100%, 40%);">+     parser.add_option("-b", "--baud", dest="baudrate", type="int", metavar="BAUD",</span><br><span style="color: hsl(120, 100%, 40%);">+                      help="Baudrate used for SIM access [default: %default]",</span><br><span style="color: hsl(120, 100%, 40%);">+                    default=9600,</span><br><span style="color: hsl(120, 100%, 40%);">+         )</span><br><span style="color: hsl(120, 100%, 40%);">+     parser.add_option("-p", "--pcsc-device", dest="pcsc_dev", type='int', metavar="PCSC",</span><br><span style="color: hsl(120, 100%, 40%);">+                 help="Which PC/SC reader number for SIM access",</span><br><span style="color: hsl(120, 100%, 40%);">+                    default=None,</span><br><span style="color: hsl(120, 100%, 40%);">+         )</span><br><span style="color: hsl(120, 100%, 40%);">+     parser.add_option("--modem-device", dest="modem_dev", metavar="DEV",</span><br><span style="color: hsl(120, 100%, 40%);">+                    help="Serial port of modem for Generic SIM Access (3GPP TS 27.007)",</span><br><span style="color: hsl(120, 100%, 40%);">+                        default=None,</span><br><span style="color: hsl(120, 100%, 40%);">+         )</span><br><span style="color: hsl(120, 100%, 40%);">+     parser.add_option("--modem-baud", dest="modem_baud", type="int", metavar="BAUD",</span><br><span style="color: hsl(120, 100%, 40%);">+                      help="Baudrate used for modem's port [default: %default]",</span><br><span style="color: hsl(120, 100%, 40%);">+                      default=115200,</span><br><span style="color: hsl(120, 100%, 40%);">+               )</span><br><span style="color: hsl(120, 100%, 40%);">+     parser.add_option("--osmocon", dest="osmocon_sock", metavar="PATH",</span><br><span style="color: hsl(120, 100%, 40%);">+                     help="Socket path for Calypso (e.g. Motorola C1XX) based reader (via OsmocomBB)",</span><br><span style="color: hsl(120, 100%, 40%);">+                   default=None,</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%);">+   parser.add_option("-a", "--pin-adm", dest="pin_adm",</span><br><span style="color: hsl(120, 100%, 40%);">+                    help="ADM PIN used for provisioning (overwrites default)",</span><br><span style="color: hsl(120, 100%, 40%);">+          )</span><br><span style="color: hsl(120, 100%, 40%);">+     parser.add_option("-A", "--pin-adm-hex", dest="pin_adm_hex",</span><br><span style="color: hsl(120, 100%, 40%);">+                    help="ADM PIN used for provisioning, as hex string (16 characters long",</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%);">+   (options, args) = parser.parse_args()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if args:</span><br><span style="color: hsl(120, 100%, 40%);">+              parser.error("Extraneous arguments")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return options</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%);">+if __name__ == '__main__':</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # Parse options</span><br><span style="color: hsl(120, 100%, 40%);">+       opts = parse_options()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      # Init card reader driver</span><br><span style="color: hsl(120, 100%, 40%);">+     sl = init_reader(opts)</span><br><span style="color: hsl(120, 100%, 40%);">+        if (sl == None):</span><br><span style="color: hsl(120, 100%, 40%);">+              exit(1)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     # Create command layer</span><br><span style="color: hsl(120, 100%, 40%);">+        scc = SimCardCommands(transport=sl)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ sl.wait_for_card();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ card_handler = card_handler(sl)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     card = card_detect("auto", scc)</span><br><span style="color: hsl(120, 100%, 40%);">+     if card is None:</span><br><span style="color: hsl(120, 100%, 40%);">+              print("No card detected!")</span><br><span style="color: hsl(120, 100%, 40%);">+          sys.exit(2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ profile = CardProfileUICC()</span><br><span style="color: hsl(120, 100%, 40%);">+   rs = RuntimeState(card, profile)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # FIXME: do this dynamically</span><br><span style="color: hsl(120, 100%, 40%);">+  rs.mf.add_file(DF_TELECOM())</span><br><span style="color: hsl(120, 100%, 40%);">+  rs.mf.add_file(DF_GSM())</span><br><span style="color: hsl(120, 100%, 40%);">+      rs.mf.add_application(ADF_USIM())</span><br><span style="color: hsl(120, 100%, 40%);">+     rs.mf.add_application(ADF_ISIM())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   app = PysimApp(card, rs)</span><br><span style="color: hsl(120, 100%, 40%);">+      app.cmdloop()</span><br><span>diff --git a/pySim/exceptions.py b/pySim/exceptions.py</span><br><span>index 5d30f76..156ec62 100644</span><br><span>--- a/pySim/exceptions.py</span><br><span>+++ b/pySim/exceptions.py</span><br><span>@@ -41,8 +41,13 @@</span><br><span> class SwMatchError(Exception):</span><br><span>        """Raised when an operation specifies an expected SW but the actual SW from</span><br><span>      the card doesn't match."""</span><br><span style="color: hsl(0, 100%, 40%);">-        def __init__(self, sw_actual, sw_expected):</span><br><span style="color: hsl(120, 100%, 40%);">+   def __init__(self, sw_actual, sw_expected, rs=None):</span><br><span>                 self.sw_actual = sw_actual</span><br><span>           self.sw_expected = sw_expected</span><br><span style="color: hsl(120, 100%, 40%);">+                self.rs = rs</span><br><span>         def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            if self.rs:</span><br><span style="color: hsl(120, 100%, 40%);">+                   r = self.rs.interpret_sw(sw_actual)</span><br><span style="color: hsl(120, 100%, 40%);">+                   if r:</span><br><span style="color: hsl(120, 100%, 40%);">+                         return "SW match failed! Expected %s and got %s: %s - %s" % (self.sw_expected, self.sw_actual, r[0], r[1])</span><br><span>                 return "SW match failed! Expected %s and got %s." % (self.sw_expected, self.sw_actual)</span><br><span>diff --git a/pySim/filesystem.py b/pySim/filesystem.py</span><br><span>new file mode 100644</span><br><span>index 0000000..2bcbe10</span><br><span>--- /dev/null</span><br><span>+++ b/pySim/filesystem.py</span><br><span>@@ -0,0 +1,715 @@</span><br><span style="color: hsl(120, 100%, 40%);">+# coding=utf-8</span><br><span style="color: hsl(120, 100%, 40%);">+"""Representation of the ISO7816-4 filesystem model.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The File (and its derived classes) represent the structure / hierarchy</span><br><span style="color: hsl(120, 100%, 40%);">+of the ISO7816-4 smart card file system with the MF, DF, EF and ADF</span><br><span style="color: hsl(120, 100%, 40%);">+entries, further sub-divided into the EF sub-types Transparent, Linear Fixed, etc.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The classes are intended to represent the *specification* of the filesystem,</span><br><span style="color: hsl(120, 100%, 40%);">+not the actual contents / runtime state of interacting with a given smart card.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+(C) 2021 by 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%);">+import code</span><br><span style="color: hsl(120, 100%, 40%);">+import json</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import cmd2</span><br><span style="color: hsl(120, 100%, 40%);">+from cmd2 import CommandSet, with_default_category, with_argparser</span><br><span style="color: hsl(120, 100%, 40%);">+import argparse</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.utils import sw_match, h2b, b2h</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.exceptions import *</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CardFile(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Base class for all objects in the smart card filesystem.</span><br><span style="color: hsl(120, 100%, 40%);">+    Serve as a common ancestor to all other file types; rarely used directly.</span><br><span style="color: hsl(120, 100%, 40%);">+    """</span><br><span style="color: hsl(120, 100%, 40%);">+    RESERVED_NAMES = ['..', '.', '/', 'MF']</span><br><span style="color: hsl(120, 100%, 40%);">+    RESERVED_FIDS = ['3f00']</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid=None, sfid=None, name=None, desc=None, parent=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if not isinstance(self, CardADF) and fid == None:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError("fid is mandatory")</span><br><span style="color: hsl(120, 100%, 40%);">+        if fid:</span><br><span style="color: hsl(120, 100%, 40%);">+            fid = fid.lower()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.fid = fid           # file identifier</span><br><span style="color: hsl(120, 100%, 40%);">+        self.sfid = sfid         # short file identifier</span><br><span style="color: hsl(120, 100%, 40%);">+        self.name = name         # human readable name</span><br><span style="color: hsl(120, 100%, 40%);">+        self.desc = desc         # human readable description</span><br><span style="color: hsl(120, 100%, 40%);">+        self.parent = parent</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.parent and self.parent != self and self.fid:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.parent.add_file(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.shell_commands = []</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.name:</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.name</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.fid</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _path_element(self, prefer_name):</span><br><span style="color: hsl(120, 100%, 40%);">+        if prefer_name and self.name:</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.name</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.fid</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def fully_qualified_path(self, prefer_name=True):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Return fully qualified path to file as list of FID or name strings."""</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.parent != self:</span><br><span style="color: hsl(120, 100%, 40%);">+            ret = self.parent.fully_qualified_path(prefer_name)</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            ret = []</span><br><span style="color: hsl(120, 100%, 40%);">+        ret.append(self._path_element(prefer_name))</span><br><span style="color: hsl(120, 100%, 40%);">+        return ret</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_mf(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Return the MF (root) of the file system."""</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.parent == None:</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</span><br><span style="color: hsl(120, 100%, 40%);">+        # iterate towards the top. MF has parent == self</span><br><span style="color: hsl(120, 100%, 40%);">+        node = self</span><br><span style="color: hsl(120, 100%, 40%);">+        while node.parent != node:</span><br><span style="color: hsl(120, 100%, 40%);">+            node = node.parent</span><br><span style="color: hsl(120, 100%, 40%);">+        return node</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _get_self_selectables(self, alias=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Return a dict of {'identifier': self} tuples"""</span><br><span style="color: hsl(120, 100%, 40%);">+        sels = {}</span><br><span style="color: hsl(120, 100%, 40%);">+        if alias:</span><br><span style="color: hsl(120, 100%, 40%);">+            sels.update({alias: self})</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.fid:</span><br><span style="color: hsl(120, 100%, 40%);">+            sels.update({self.fid: self})</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.name:</span><br><span style="color: hsl(120, 100%, 40%);">+            sels.update({self.name: self})</span><br><span style="color: hsl(120, 100%, 40%);">+        return sels</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_selectables(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Return a dict of {'identifier': File} that is selectable from the current file."""</span><br><span style="color: hsl(120, 100%, 40%);">+        # we can always select ourself</span><br><span style="color: hsl(120, 100%, 40%);">+        sels = self._get_self_selectables('.')</span><br><span style="color: hsl(120, 100%, 40%);">+        # we can always select our parent</span><br><span style="color: hsl(120, 100%, 40%);">+        sels = self.parent._get_self_selectables('..')</span><br><span style="color: hsl(120, 100%, 40%);">+        # if we have a MF, we can always select its applications</span><br><span style="color: hsl(120, 100%, 40%);">+        mf = self.get_mf()</span><br><span style="color: hsl(120, 100%, 40%);">+        if mf:</span><br><span style="color: hsl(120, 100%, 40%);">+            sels.update(mf._get_self_selectables())</span><br><span style="color: hsl(120, 100%, 40%);">+            sels.update(mf.get_app_selectables())</span><br><span style="color: hsl(120, 100%, 40%);">+        return sels</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_selectable_names(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Return a list of strings for all identifiers that are selectable from the current file."""</span><br><span style="color: hsl(120, 100%, 40%);">+        sels = self.get_selectables()</span><br><span style="color: hsl(120, 100%, 40%);">+        return sels.keys()</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%);">+        """Decode the response to a SELECT command."""</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.parent.decode_select_response(data_hex)</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 CardDF(CardFile):</span><br><span style="color: hsl(120, 100%, 40%);">+    """DF (Dedicated File) in the smart card filesystem.  Those are basically sub-directories."""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, **kwargs):</span><br><span style="color: hsl(120, 100%, 40%);">+        if not isinstance(self, CardADF):</span><br><span style="color: hsl(120, 100%, 40%);">+            if not 'fid' in kwargs:</span><br><span style="color: hsl(120, 100%, 40%);">+                raise TypeError('fid is mandatory for all DF')</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(**kwargs)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.children = dict()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return "DF(%s)" % (super().__str__())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def add_file(self, child, ignore_existing=False):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Add a child (DF/EF) to this DF"""</span><br><span style="color: hsl(120, 100%, 40%);">+        if not isinstance(child, CardFile):</span><br><span style="color: hsl(120, 100%, 40%);">+            raise TypeError("Expected a File instance")</span><br><span style="color: hsl(120, 100%, 40%);">+        if child.name in CardFile.RESERVED_NAMES:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError("File name %s is a reserved name" % (child.name))</span><br><span style="color: hsl(120, 100%, 40%);">+        if child.fid in CardFile.RESERVED_FIDS:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError("File fid %s is a reserved name" % (child.fid))</span><br><span style="color: hsl(120, 100%, 40%);">+        if child.fid in self.children:</span><br><span style="color: hsl(120, 100%, 40%);">+            if ignore_existing:</span><br><span style="color: hsl(120, 100%, 40%);">+                return</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError("File with given fid %s already exists" % (child.fid))</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.lookup_file_by_sfid(child.sfid):</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError("File with given sfid %s already exists" % (child.sfid))</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.lookup_file_by_name(child.name):</span><br><span style="color: hsl(120, 100%, 40%);">+            if ignore_existing:</span><br><span style="color: hsl(120, 100%, 40%);">+                return</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError("File with given name %s already exists" % (child.name))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.children[child.fid] = child</span><br><span style="color: hsl(120, 100%, 40%);">+        child.parent = self</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def add_files(self, children, ignore_existing=False):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Add a list of child (DF/EF) to this DF"""</span><br><span style="color: hsl(120, 100%, 40%);">+        for child in children:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.add_file(child, ignore_existing)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_selectables(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Get selectable (DF/EF names) from current DF"""</span><br><span style="color: hsl(120, 100%, 40%);">+        # global selectables + our children</span><br><span style="color: hsl(120, 100%, 40%);">+        sels = super().get_selectables()</span><br><span style="color: hsl(120, 100%, 40%);">+        sels.update({x.fid: x for x in self.children.values() if x.fid})</span><br><span style="color: hsl(120, 100%, 40%);">+        sels.update({x.name: x for x in self.children.values() if x.name})</span><br><span style="color: hsl(120, 100%, 40%);">+        return sels</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def lookup_file_by_name(self, name):</span><br><span style="color: hsl(120, 100%, 40%);">+        if name == None:</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</span><br><span style="color: hsl(120, 100%, 40%);">+        for i in self.children.values():</span><br><span style="color: hsl(120, 100%, 40%);">+            if i.name and i.name == name:</span><br><span style="color: hsl(120, 100%, 40%);">+                return i</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%);">+    def lookup_file_by_sfid(self, sfid):</span><br><span style="color: hsl(120, 100%, 40%);">+        if sfid == None:</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</span><br><span style="color: hsl(120, 100%, 40%);">+        for i in self.children.values():</span><br><span style="color: hsl(120, 100%, 40%);">+            if i.sfid == int(sfid):</span><br><span style="color: hsl(120, 100%, 40%);">+                return i</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%);">+    def lookup_file_by_fid(self, fid):</span><br><span style="color: hsl(120, 100%, 40%);">+        if fid in self.children:</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.children[fid]</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CardMF(CardDF):</span><br><span style="color: hsl(120, 100%, 40%);">+    """MF (Master File) in the smart card filesystem"""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, **kwargs):</span><br><span style="color: hsl(120, 100%, 40%);">+        # can be overridden; use setdefault</span><br><span style="color: hsl(120, 100%, 40%);">+        kwargs.setdefault('fid', '3f00')</span><br><span style="color: hsl(120, 100%, 40%);">+        kwargs.setdefault('name', 'MF')</span><br><span style="color: hsl(120, 100%, 40%);">+        kwargs.setdefault('desc', 'Master File (directory root)')</span><br><span style="color: hsl(120, 100%, 40%);">+        # cannot be overridden; use assignment</span><br><span style="color: hsl(120, 100%, 40%);">+        kwargs['parent'] = self</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(**kwargs)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.applications = dict()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return "MF(%s)" % (self.fid)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def add_application(self, app):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Add an ADF (Application Dedicated File) to the MF"""</span><br><span style="color: hsl(120, 100%, 40%);">+        if not isinstance(app, CardADF):</span><br><span style="color: hsl(120, 100%, 40%);">+            raise TypeError("Expected an ADF instance")</span><br><span style="color: hsl(120, 100%, 40%);">+        if app.aid in self.applications:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError("AID %s already exists" % (app.aid))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.applications[app.aid] = app</span><br><span style="color: hsl(120, 100%, 40%);">+        app.parent=self</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_app_names(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Get list of completions (AID names)"""</span><br><span style="color: hsl(120, 100%, 40%);">+        return [x.name for x in self.applications]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_selectables(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Get list of completions (DF/EF/ADF names) from current DF"""</span><br><span style="color: hsl(120, 100%, 40%);">+        sels = super().get_selectables()</span><br><span style="color: hsl(120, 100%, 40%);">+        sels.update(self.get_app_selectables())</span><br><span style="color: hsl(120, 100%, 40%);">+        return sels</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_app_selectables(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        # applications by AID + name</span><br><span style="color: hsl(120, 100%, 40%);">+        sels = {x.aid: x for x in self.applications.values()}</span><br><span style="color: hsl(120, 100%, 40%);">+        sels.update({x.name: x for x in self.applications.values() if x.name})</span><br><span style="color: hsl(120, 100%, 40%);">+        return sels</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%);">+        """Decode the response to a SELECT command."""</span><br><span style="color: hsl(120, 100%, 40%);">+        return data_hex</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 CardADF(CardDF):</span><br><span style="color: hsl(120, 100%, 40%);">+    """ADF (Application Dedicated File) in the smart card filesystem"""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, aid, **kwargs):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(**kwargs)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.aid = aid           # Application Identifier</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.parent:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.parent.add_application(self)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return "ADF(%s)" % (self.aid)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _path_element(self, prefer_name):</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.name and prefer_name:</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.name</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.aid</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 CardEF(CardFile):</span><br><span style="color: hsl(120, 100%, 40%);">+    """EF (Entry File) in the smart card filesystem"""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, *, fid, **kwargs):</span><br><span style="color: hsl(120, 100%, 40%);">+        kwargs['fid'] = fid</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(**kwargs)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return "EF(%s)" % (super().__str__())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_selectables(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Get list of completions (EF names) from current DF"""</span><br><span style="color: hsl(120, 100%, 40%);">+        #global selectable names + those of the parent DF</span><br><span style="color: hsl(120, 100%, 40%);">+        sels = super().get_selectables()</span><br><span style="color: hsl(120, 100%, 40%);">+        sels.update({x.name:x for x in self.parent.children.values() if x != self})</span><br><span style="color: hsl(120, 100%, 40%);">+        return sels</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 TransparentEF(CardEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Transparent EF (Entry File) in the smart card filesystem"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @with_default_category('Transparent EF Commands')</span><br><span style="color: hsl(120, 100%, 40%);">+    class ShellCommands(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%);">+        read_bin_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        read_bin_parser.add_argument('--offset', type=int, default=0, help='Byte offset for start of read')</span><br><span style="color: hsl(120, 100%, 40%);">+        read_bin_parser.add_argument('--length', type=int, help='Number of bytes to read')</span><br><span style="color: hsl(120, 100%, 40%);">+        @cmd2.with_argparser(read_bin_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_read_binary(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Read binary data from a transparent EF"""</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.rs.read_binary(opts.length, opts.offset)</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput(data)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_read_binary_decoded(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Read + decode data from a transparent EF"""</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.rs.read_binary_dec()</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput(json.dumps(data, indent=4))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_bin_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_bin_parser.add_argument('--offset', type=int, default=0, help='Byte offset for start of read')</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_bin_parser.add_argument('data', help='Data bytes (hex format) to write')</span><br><span style="color: hsl(120, 100%, 40%);">+        @cmd2.with_argparser(upd_bin_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_update_binary(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Update (Write) data of a transparent EF"""</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.rs.update_binary(opts.data, opts.offset)</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput(data)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_bin_dec_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_bin_dec_parser.add_argument('data', help='Abstract data (JSON format) to write')</span><br><span style="color: hsl(120, 100%, 40%);">+        @cmd2.with_argparser(upd_bin_dec_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_update_binary_decoded(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Encode + Update (Write) data of a transparent EF"""</span><br><span style="color: hsl(120, 100%, 40%);">+            data_json = json.loads(opts.data)</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.rs.update_binary_dec(data_json)</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput(json.dumps(data, indent=4))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid, sfid=None, name=None, desc=None, parent=None, size={1,None}):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.size = size</span><br><span style="color: hsl(120, 100%, 40%);">+        self.shell_commands = [self.ShellCommands()]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode_bin(self, raw_bin_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode raw (binary) data into abstract representation. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_decode_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_bin_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_decode_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(b2h(raw_bin_data))</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'raw': raw_bin_data.hex()}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode_hex(self, raw_hex_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode raw (hex string) data into abstract representation. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_decode_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_hex_data)</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%);">+        method = getattr(self, '_decode_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_bin_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'raw': raw_bin_data.hex()}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode_bin(self, abstract_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Encode abstract representation into raw (binary) data. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(abstract_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return h2b(method(abstract_data))</span><br><span style="color: hsl(120, 100%, 40%);">+        raise NotImplementedError</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode_hex(self, abstract_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Encode abstract representation into raw (hex string) data. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(abstract_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            raw_bin_data = method(abstract_data)</span><br><span style="color: hsl(120, 100%, 40%);">+            return b2h(raw_bin_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        raise NotImplementedError</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 LinFixedEF(CardEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Linear Fixed EF (Entry File) in the smart card filesystem"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @with_default_category('Linear Fixed EF Commands')</span><br><span style="color: hsl(120, 100%, 40%);">+    class ShellCommands(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%);">+        read_rec_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        read_rec_parser.add_argument('record_nr', type=int, help='Number of record to be read')</span><br><span style="color: hsl(120, 100%, 40%);">+        @cmd2.with_argparser(read_rec_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_read_record(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Read a record from a record-oriented EF"""</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.rs.read_record(opts.record_nr)</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput(data)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        read_rec_dec_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        read_rec_dec_parser.add_argument('record_nr', type=int, help='Number of record to be read')</span><br><span style="color: hsl(120, 100%, 40%);">+        @cmd2.with_argparser(read_rec_dec_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_read_record_decoded(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Read + decode a record from a record-oriented EF"""</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.rs.read_record_dec(opts.record_nr)</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput(json.dumps(data, indent=4))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_rec_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_rec_parser.add_argument('record_nr', type=int, help='Number of record to be read')</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_rec_parser.add_argument('data', help='Data bytes (hex format) to write')</span><br><span style="color: hsl(120, 100%, 40%);">+        @cmd2.with_argparser(upd_rec_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_update_record(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Update (write) data to a record-oriented EF"""</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.rs.update_record(opts.record_nr, opts.data)</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput(data)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_rec_dec_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_rec_dec_parser.add_argument('record_nr', type=int, help='Number of record to be read')</span><br><span style="color: hsl(120, 100%, 40%);">+        upd_rec_dec_parser.add_argument('data', help='Data bytes (hex format) to write')</span><br><span style="color: hsl(120, 100%, 40%);">+        @cmd2.with_argparser(upd_rec_dec_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_update_record_decoded(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Encode + Update (write) data to a record-oriented EF"""</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.rs.update_record_dec(opts.record_nr, opts.data)</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput(data)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid, sfid=None, name=None, desc=None, parent=None, rec_len={1,None}):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.rec_len = rec_len</span><br><span style="color: hsl(120, 100%, 40%);">+        self.shell_commands = [self.ShellCommands()]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode_record_hex(self, raw_hex_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode raw (hex string) data into abstract representation. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_decode_record_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_hex_data)</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%);">+        method = getattr(self, '_decode_record_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_bin_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'raw': raw_bin_data.hex()}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode_record_bin(self, raw_bin_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode raw (binary) data into abstract representation. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_decode_record_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_bin_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%);">+        method = getattr(self, '_decode_record_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_hex_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'raw': raw_hex_data}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode_record_hex(self, abstract_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Encode abstract representation into raw (hex string) data. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_record_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(abstract_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_record_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            raw_bin_data = method(abstract_data)</span><br><span style="color: hsl(120, 100%, 40%);">+            return b2h(raww_bin_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        raise NotImplementedError</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode_record_bin(self, abstract_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Encode abstract representation into raw (binary) data. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_record_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(abstract_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_record_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return b2h(method(abstract_data))</span><br><span style="color: hsl(120, 100%, 40%);">+        raise NotImplementedError</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CyclicEF(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Cyclic EF (Entry File) in the smart card filesystem"""</span><br><span style="color: hsl(120, 100%, 40%);">+    # we don't really have any special support for those; just recycling LinFixedEF here</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid, sfid=None, name=None, desc=None, parent=None, rec_len={1,None}):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, rec_len=rec_len)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TransRecEF(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Transparent EF (Entry File) containing fixed-size records.</span><br><span style="color: hsl(120, 100%, 40%);">+    These are the real odd-balls and mostly look like mistakes in the specification:</span><br><span style="color: hsl(120, 100%, 40%);">+    Specified as 'transparent' EF, but actually containing several fixed-length records</span><br><span style="color: hsl(120, 100%, 40%);">+    inside.</span><br><span style="color: hsl(120, 100%, 40%);">+    We add a special class for those, so the user only has to provide encoder/decoder functions</span><br><span style="color: hsl(120, 100%, 40%);">+    for a record, while this class takes care of split / merge of records.</span><br><span style="color: hsl(120, 100%, 40%);">+    """</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid, sfid=None, name=None, desc=None, parent=None, rec_len=None, size={1,None}):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, size=size)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.rec_len = rec_len</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode_record_hex(self, raw_hex_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode raw (hex string) data into abstract representation. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_decode_record_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_hex_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_decode_record_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</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%);">+            return method(raw_bin_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'raw': raw_hex_data}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode_record_bin(self, raw_bin_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode raw (hex string) data into abstract representation. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_decode_record_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_bin_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%);">+        method = getattr(self, '_decode_record_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(raw_hex_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'raw': raw_hex_data}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode_record_hex(self, abstract_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Encode abstract representation into raw (hex string) data. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_record_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(abstract_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_record_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return h2b(method(abstract_data))</span><br><span style="color: hsl(120, 100%, 40%);">+        raise NotImplementedError</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode_record_bin(self, abstract_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Encode abstract representation into raw (binary) data. Overloaded by specific classes."""</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_record_bin', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return method(abstract_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = getattr(self, '_encode_record_hex', None)</span><br><span style="color: hsl(120, 100%, 40%);">+        if callable(method):</span><br><span style="color: hsl(120, 100%, 40%);">+            return h2b(method(abstract_data))</span><br><span style="color: hsl(120, 100%, 40%);">+        raise NotImplementedError</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_bin(self, raw_bin_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        chunks = [raw_bin_data[i:i+self.rec_len] for i in range(0, len(raw_bin_data), self.rec_len)]</span><br><span style="color: hsl(120, 100%, 40%);">+        return [self.decode_record_bin(x) for x in chunks]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_bin(self, abstract_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        chunks = [self.encode_record_bin(x) for x in abstract_data]</span><br><span style="color: hsl(120, 100%, 40%);">+        # FIXME: pad to file size</span><br><span style="color: hsl(120, 100%, 40%);">+        return b''.join(chunks)</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%);">+class RuntimeState(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Represent the runtime state of a session with a card."""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, card, profile):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.mf = CardMF()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.card = card</span><br><span style="color: hsl(120, 100%, 40%);">+        self.selected_file = self.mf</span><br><span style="color: hsl(120, 100%, 40%);">+        self.profile = profile</span><br><span style="color: hsl(120, 100%, 40%);">+        # add applications + MF-files from profile</span><br><span style="color: hsl(120, 100%, 40%);">+        for a in self.profile.applications:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.mf.add_application(a)</span><br><span style="color: hsl(120, 100%, 40%);">+        for f in self.profile.files_in_mf:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.mf.add_file(f)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_cwd(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Obtain the current working directory."""</span><br><span style="color: hsl(120, 100%, 40%);">+        if isinstance(self.selected_file, CardDF):</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.selected_file</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.selected_file.parent</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_application(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Obtain the currently selected application (if any)."""</span><br><span style="color: hsl(120, 100%, 40%);">+        # iterate upwards from selected file; check if any is an ADF</span><br><span style="color: hsl(120, 100%, 40%);">+        node = self.selected_file</span><br><span style="color: hsl(120, 100%, 40%);">+        while node.parent != node:</span><br><span style="color: hsl(120, 100%, 40%);">+            if isinstance(node, CardADF):</span><br><span style="color: hsl(120, 100%, 40%);">+                return node</span><br><span style="color: hsl(120, 100%, 40%);">+            node = node.parent</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%);">+    def interpret_sw(self, sw):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Interpret the given SW relative to the currently selected Application</span><br><span style="color: hsl(120, 100%, 40%);">+           or the underlying profile."""</span><br><span style="color: hsl(120, 100%, 40%);">+        app = self.get_application()</span><br><span style="color: hsl(120, 100%, 40%);">+        if app:</span><br><span style="color: hsl(120, 100%, 40%);">+            # The application either comes with its own interpret_sw</span><br><span style="color: hsl(120, 100%, 40%);">+            # method or we will use the interpret_sw method from the</span><br><span style="color: hsl(120, 100%, 40%);">+            # card profile.</span><br><span style="color: hsl(120, 100%, 40%);">+            if hasattr(app, "interpret_sw"):</span><br><span style="color: hsl(120, 100%, 40%);">+                return app.interpret_sw(sw)</span><br><span style="color: hsl(120, 100%, 40%);">+            else:</span><br><span style="color: hsl(120, 100%, 40%);">+                return self.profile.interpret_sw(sw)</span><br><span style="color: hsl(120, 100%, 40%);">+            return app.interpret_sw(sw)</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.profile.interpret_sw(sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def select(self, name, cmd_app=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Change current directory"""</span><br><span style="color: hsl(120, 100%, 40%);">+        sels = self.selected_file.get_selectables()</span><br><span style="color: hsl(120, 100%, 40%);">+        if name in sels:</span><br><span style="color: hsl(120, 100%, 40%);">+            f = sels[name]</span><br><span style="color: hsl(120, 100%, 40%);">+            # unregister commands of old file</span><br><span style="color: hsl(120, 100%, 40%);">+            if cmd_app and self.selected_file.shell_commands:</span><br><span style="color: hsl(120, 100%, 40%);">+                for c in self.selected_file.shell_commands:</span><br><span style="color: hsl(120, 100%, 40%);">+                    cmd_app.unregister_command_set(c)</span><br><span style="color: hsl(120, 100%, 40%);">+            try:</span><br><span style="color: hsl(120, 100%, 40%);">+                if isinstance(f, CardADF):</span><br><span style="color: hsl(120, 100%, 40%);">+                    (data, sw) = self.card._scc.select_adf(f.aid)</span><br><span style="color: hsl(120, 100%, 40%);">+                else:</span><br><span style="color: hsl(120, 100%, 40%);">+                    (data, sw) = self.card._scc.select_file(f.fid)</span><br><span style="color: hsl(120, 100%, 40%);">+                self.selected_file = f</span><br><span style="color: hsl(120, 100%, 40%);">+            except SwMatchError as swm:</span><br><span style="color: hsl(120, 100%, 40%);">+                k = self.interpret_sw(swm.sw_actual)</span><br><span style="color: hsl(120, 100%, 40%);">+                if not k:</span><br><span style="color: hsl(120, 100%, 40%);">+                    raise(swm)</span><br><span style="color: hsl(120, 100%, 40%);">+                raise RuntimeError("%s: %s - %s" % (swm.sw_actual, k[0], k[1]))</span><br><span style="color: hsl(120, 100%, 40%);">+            # register commands of new file</span><br><span style="color: hsl(120, 100%, 40%);">+            if cmd_app and self.selected_file.shell_commands:</span><br><span style="color: hsl(120, 100%, 40%);">+                for c in self.selected_file.shell_commands:</span><br><span style="color: hsl(120, 100%, 40%);">+                    cmd_app.register_command_set(c)</span><br><span style="color: hsl(120, 100%, 40%);">+            return f.decode_select_response(data)</span><br><span style="color: hsl(120, 100%, 40%);">+        #elif looks_like_fid(name):</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError("Cannot select unknown %s" % (name))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def read_binary(self, length=None, offset=0):</span><br><span style="color: hsl(120, 100%, 40%);">+        if not isinstance(self.selected_file, TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+            raise TypeError("Only works with TransparentEF")</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.card._scc.read_binary(self.selected_file.fid, length, offset)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def read_binary_dec(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        (data, sw) = self.read_binary()</span><br><span style="color: hsl(120, 100%, 40%);">+        dec_data = self.selected_file.decode_hex(data)</span><br><span style="color: hsl(120, 100%, 40%);">+        print("%s: %s -> %s" % (sw, data, dec_data))</span><br><span style="color: hsl(120, 100%, 40%);">+        return (dec_data, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def update_binary(self, data_hex, offset=0):</span><br><span style="color: hsl(120, 100%, 40%);">+        if not isinstance(self.selected_file, TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+            raise TypeError("Only works with TransparentEF")</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.card._scc.update_binary(self.selected_file.fid, data_hex, offset)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def update_binary_dec(self, data):</span><br><span style="color: hsl(120, 100%, 40%);">+        data_hex = self.selected_file.encode_hex(data)</span><br><span style="color: hsl(120, 100%, 40%);">+        print("%s -> %s" % (data, data_hex))</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.update_binary(data_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def read_record(self, rec_nr=0):</span><br><span style="color: hsl(120, 100%, 40%);">+        if not isinstance(self.selected_file, LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+            raise TypeError("Only works with Linear Fixed EF")</span><br><span style="color: hsl(120, 100%, 40%);">+        # returns a string of hex nibbles</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.card._scc.read_record(self.selected_file.fid, rec_nr)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def read_record_dec(self, rec_nr=0):</span><br><span style="color: hsl(120, 100%, 40%);">+        (data, sw) = self.read_record(rec_nr)</span><br><span style="color: hsl(120, 100%, 40%);">+        return (self.selected_file.decode_record_hex(data), sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def update_record(self, rec_nr, data_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+        if not isinstance(self.selected_file, LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+            raise TypeError("Only works with Linear Fixed EF")</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.card._scc.update_record(self.selected_file.fid, rec_nr, data_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def update_record_dec(self, rec_nr, data):</span><br><span style="color: hsl(120, 100%, 40%);">+        hex_data = self.selected_file.encode_record_hex(data)</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.update_record(self, rec_nr, data_hex)</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 FileData(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Represent the runtime, on-card data."""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fdesc):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.desc = fdesc</span><br><span style="color: hsl(120, 100%, 40%);">+        self.fcp = None</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 interpret_sw(sw_data, sw):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Interpret a given status word within the profile.  Returns tuple of</span><br><span style="color: hsl(120, 100%, 40%);">+       two strings"""</span><br><span style="color: hsl(120, 100%, 40%);">+    for class_str, swdict in sw_data.items():</span><br><span style="color: hsl(120, 100%, 40%);">+        # first try direct match</span><br><span style="color: hsl(120, 100%, 40%);">+        if sw in swdict:</span><br><span style="color: hsl(120, 100%, 40%);">+            return (class_str, swdict[sw])</span><br><span style="color: hsl(120, 100%, 40%);">+        # next try wildcard matches</span><br><span style="color: hsl(120, 100%, 40%);">+        for pattern, descr in swdict.items():</span><br><span style="color: hsl(120, 100%, 40%);">+            if sw_match(sw, pattern):</span><br><span style="color: hsl(120, 100%, 40%);">+                return (class_str, descr)</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%);">+class CardApplication(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """A card application is represented by an ADF (with contained hierarchy) and optionally</span><br><span style="color: hsl(120, 100%, 40%);">+       some SW definitions."""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, name, adf=None, sw={}):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.name = name</span><br><span style="color: hsl(120, 100%, 40%);">+        self.adf = adf</span><br><span style="color: hsl(120, 100%, 40%);">+        self.sw = sw</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return "APP(%s)" % (self.name)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def interpret_sw(self, sw):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Interpret a given status word within the application.  Returns tuple of</span><br><span style="color: hsl(120, 100%, 40%);">+           two strings"""</span><br><span style="color: hsl(120, 100%, 40%);">+        return interpret_sw(self.sw, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CardProfile(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """A Card Profile describes a card, it's filessystem hierarchy, an [initial] list of</span><br><span style="color: hsl(120, 100%, 40%);">+       applications as well as profile-specific SW and shell commands.  Every card has</span><br><span style="color: hsl(120, 100%, 40%);">+       one card profile, but there may be multiple applications within that profile."""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, name, desc=None, files_in_mf=[], sw=[], applications=[], shell_cmdsets=[]):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.name = name</span><br><span style="color: hsl(120, 100%, 40%);">+        self.desc = desc</span><br><span style="color: hsl(120, 100%, 40%);">+        self.files_in_mf = files_in_mf</span><br><span style="color: hsl(120, 100%, 40%);">+        self.sw = sw</span><br><span style="color: hsl(120, 100%, 40%);">+        self.applications = applications</span><br><span style="color: hsl(120, 100%, 40%);">+        self.shell_cmdsets = shell_cmdsets</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.name</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def add_application(self, app):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.applications.add(app)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def interpret_sw(self, sw):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Interpret a given status word within the profile.  Returns tuple of</span><br><span style="color: hsl(120, 100%, 40%);">+           two strings"""</span><br><span style="color: hsl(120, 100%, 40%);">+        return interpret_sw(self.sw, sw)</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%);">+if __name__ == '__main__':</span><br><span style="color: hsl(120, 100%, 40%);">+    mf = CardMF()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    adf_usim = ADF('a0000000871002', name='ADF_USIM')</span><br><span style="color: hsl(120, 100%, 40%);">+    mf.add_application(adf_usim)</span><br><span style="color: hsl(120, 100%, 40%);">+    df_pb = CardDF('5f3a', name='DF.PHONEBOOK')</span><br><span style="color: hsl(120, 100%, 40%);">+    adf_usim.add_file(df_pb)</span><br><span style="color: hsl(120, 100%, 40%);">+    adf_usim.add_file(TransparentEF('6f05', name='EF.LI', size={2,16}))</span><br><span style="color: hsl(120, 100%, 40%);">+    adf_usim.add_file(TransparentEF('6f07', name='EF.IMSI', size={9,9}))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    rss = RuntimeState(mf, None)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    interp = code.InteractiveConsole(locals={'mf':mf, 'rss':rss})</span><br><span style="color: hsl(120, 100%, 40%);">+    interp.interact()</span><br><span>diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py</span><br><span>new file mode 100644</span><br><span>index 0000000..256a697</span><br><span>--- /dev/null</span><br><span>+++ b/pySim/ts_102_221.py</span><br><span>@@ -0,0 +1,297 @@</span><br><span style="color: hsl(120, 100%, 40%);">+# coding=utf-8</span><br><span style="color: hsl(120, 100%, 40%);">+"""Utilities / Functions related to ETSI TS 102 221, the core UICC spec.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+(C) 2021 by 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%);">+from pytlv.TLV import *</span><br><span style="color: hsl(120, 100%, 40%);">+from struct import pack, unpack</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.utils import *</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.filesystem import *</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%);">+FCP_TLV_MAP = {</span><br><span style="color: hsl(120, 100%, 40%);">+    '82': 'file_descriptor',</span><br><span style="color: hsl(120, 100%, 40%);">+    '83': 'file_identifier',</span><br><span style="color: hsl(120, 100%, 40%);">+    '84': 'df_name',</span><br><span style="color: hsl(120, 100%, 40%);">+    'A5': 'proprietary_info',</span><br><span style="color: hsl(120, 100%, 40%);">+    '8A': 'life_cycle_status_int',</span><br><span style="color: hsl(120, 100%, 40%);">+    '8B': 'security_attrib_ref_expanded',</span><br><span style="color: hsl(120, 100%, 40%);">+    '8C': 'security_attrib_compact',</span><br><span style="color: hsl(120, 100%, 40%);">+    'AB': 'security_attrib_espanded',</span><br><span style="color: hsl(120, 100%, 40%);">+    'C6': 'pin_status_template_do',</span><br><span style="color: hsl(120, 100%, 40%);">+    '80': 'file_size',</span><br><span style="color: hsl(120, 100%, 40%);">+    '81': 'total_file_size',</span><br><span style="color: hsl(120, 100%, 40%);">+    '88': 'short_file_id',</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%);">+# ETSI TS 102 221 11.1.1.4.6</span><br><span style="color: hsl(120, 100%, 40%);">+FCP_Proprietary_TLV_MAP = {</span><br><span style="color: hsl(120, 100%, 40%);">+    '80': 'uicc_characteristics',</span><br><span style="color: hsl(120, 100%, 40%);">+    '81': 'application_power_consumption',</span><br><span style="color: hsl(120, 100%, 40%);">+    '82': 'minimum_app_clock_freq',</span><br><span style="color: hsl(120, 100%, 40%);">+    '83': 'available_memory',</span><br><span style="color: hsl(120, 100%, 40%);">+    '84': 'file_details',</span><br><span style="color: hsl(120, 100%, 40%);">+    '85': 'reserved_file_size',</span><br><span style="color: hsl(120, 100%, 40%);">+    '86': 'maximum_file_size',</span><br><span style="color: hsl(120, 100%, 40%);">+    '87': 'suported_system_commands',</span><br><span style="color: hsl(120, 100%, 40%);">+    '88': 'specific_uicc_env_cond',</span><br><span style="color: hsl(120, 100%, 40%);">+    '89': 'p2p_cat_secured_apdu',</span><br><span style="color: hsl(120, 100%, 40%);">+    # Additional private TLV objects (bits b7 and b8 of the first byte of the tag set to '1')</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%);">+# ETSI TS 102 221 11.1.1.4.3</span><br><span style="color: hsl(120, 100%, 40%);">+def interpret_file_descriptor(in_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+    in_bin = h2b(in_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+    out = {}</span><br><span style="color: hsl(120, 100%, 40%);">+    ft_dict = {</span><br><span style="color: hsl(120, 100%, 40%);">+        0: 'working_ef',</span><br><span style="color: hsl(120, 100%, 40%);">+        1: 'internal_ef',</span><br><span style="color: hsl(120, 100%, 40%);">+        7: 'df'</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    fs_dict = {</span><br><span style="color: hsl(120, 100%, 40%);">+        0: 'no_info_given',</span><br><span style="color: hsl(120, 100%, 40%);">+        1: 'transparent',</span><br><span style="color: hsl(120, 100%, 40%);">+        2: 'linear_fixed',</span><br><span style="color: hsl(120, 100%, 40%);">+        6: 'cyclic',</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    fdb = in_bin[0]</span><br><span style="color: hsl(120, 100%, 40%);">+    ftype = (fdb >> 3) & 7</span><br><span style="color: hsl(120, 100%, 40%);">+    fstruct = fdb & 7</span><br><span style="color: hsl(120, 100%, 40%);">+    out['shareable'] = True if fdb & 0x40 else False</span><br><span style="color: hsl(120, 100%, 40%);">+    out['file_type'] = ft_dict[ftype] if ftype in ft_dict else ftype</span><br><span style="color: hsl(120, 100%, 40%);">+    out['structure'] = fs_dict[fstruct] if fstruct in fs_dict else fstruct</span><br><span style="color: hsl(120, 100%, 40%);">+    if len(in_bin) >= 5:</span><br><span style="color: hsl(120, 100%, 40%);">+        out['record_len'] = int.from_bytes(in_bin[2:4], 'big')</span><br><span style="color: hsl(120, 100%, 40%);">+        out['num_of_rec'] = int.from_bytes(in_bin[4:5], 'big')</span><br><span style="color: hsl(120, 100%, 40%);">+    return out</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# ETSI TS 102 221 11.1.1.4.9</span><br><span style="color: hsl(120, 100%, 40%);">+def interpret_life_cycle_sts_int(in_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+    lcsi = int(in_hex, 16)</span><br><span style="color: hsl(120, 100%, 40%);">+    if lcsi == 0x00:</span><br><span style="color: hsl(120, 100%, 40%);">+        return 'no_information'</span><br><span style="color: hsl(120, 100%, 40%);">+    elif lcsi == 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+        return 'creation'</span><br><span style="color: hsl(120, 100%, 40%);">+    elif lcsi == 0x03:</span><br><span style="color: hsl(120, 100%, 40%);">+        return 'initialization'</span><br><span style="color: hsl(120, 100%, 40%);">+    elif lcsi & 0x05 == 0x05:</span><br><span style="color: hsl(120, 100%, 40%);">+        return 'operational_activated'</span><br><span style="color: hsl(120, 100%, 40%);">+    elif lcsi & 0x05 == 0x04:</span><br><span style="color: hsl(120, 100%, 40%);">+        return 'operational_deactivated'</span><br><span style="color: hsl(120, 100%, 40%);">+    elif lcsi & 0xc0 == 0xc0:</span><br><span style="color: hsl(120, 100%, 40%);">+        return 'termination'</span><br><span style="color: hsl(120, 100%, 40%);">+    else:</span><br><span style="color: hsl(120, 100%, 40%);">+        return in_hex</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# ETSI TS 102 221 11.1.1.4.10</span><br><span style="color: hsl(120, 100%, 40%);">+FCP_Pin_Status_TLV_MAP = {</span><br><span style="color: hsl(120, 100%, 40%);">+    '90': 'ps_do',</span><br><span style="color: hsl(120, 100%, 40%);">+    '95': 'usage_qualifier',</span><br><span style="color: hsl(120, 100%, 40%);">+    '83': 'key_reference',</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 interpret_ps_templ_do(in_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+    # cannot use the 'TLV' parser due to repeating tags</span><br><span style="color: hsl(120, 100%, 40%);">+    #psdo_tlv = TLV(FCP_Pin_Status_TLV_MAP)</span><br><span style="color: hsl(120, 100%, 40%);">+    #return psdo_tlv.parse(in_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+    return in_hex</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# 'interpreter' functions for each tag</span><br><span style="color: hsl(120, 100%, 40%);">+FCP_interpreter_map = {</span><br><span style="color: hsl(120, 100%, 40%);">+    '80': lambda x: int(x, 16),</span><br><span style="color: hsl(120, 100%, 40%);">+    '82': interpret_file_descriptor,</span><br><span style="color: hsl(120, 100%, 40%);">+    '8A': interpret_life_cycle_sts_int,</span><br><span style="color: hsl(120, 100%, 40%);">+    'C6': interpret_ps_templ_do,</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%);">+FCP_prorietary_interpreter_map = {</span><br><span style="color: hsl(120, 100%, 40%);">+    '83': lambda x: int(x, 16),</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%);">+# pytlv unfortunately doesn't have a setting using which we can make it</span><br><span style="color: hsl(120, 100%, 40%);">+# accept unknown tags.  It also doesn't raise a specific exception type but</span><br><span style="color: hsl(120, 100%, 40%);">+# just the generic ValueError, so we cannot ignore those either.  Instead,</span><br><span style="color: hsl(120, 100%, 40%);">+# we insert a dict entry for every possible proprietary tag permitted</span><br><span style="color: hsl(120, 100%, 40%);">+def fixup_fcp_proprietary_tlv_map(tlv_map):</span><br><span style="color: hsl(120, 100%, 40%);">+    if 'D0' in tlv_map:</span><br><span style="color: hsl(120, 100%, 40%);">+        return</span><br><span style="color: hsl(120, 100%, 40%);">+    for i in range(0xd0, 0xff):</span><br><span style="color: hsl(120, 100%, 40%);">+        i_hex = i2h([i]).upper()</span><br><span style="color: hsl(120, 100%, 40%);">+        tlv_map[i_hex] = 'proprietary_' + i_hex</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 tlv_key_replace(inmap, indata):</span><br><span style="color: hsl(120, 100%, 40%);">+    def newkey(inmap, key):</span><br><span style="color: hsl(120, 100%, 40%);">+        if key in inmap:</span><br><span style="color: hsl(120, 100%, 40%);">+            return inmap[key]</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return key</span><br><span style="color: hsl(120, 100%, 40%);">+    return {newkey(inmap, d[0]): d[1] for d in indata.items()}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def tlv_val_interpret(inmap, indata):</span><br><span style="color: hsl(120, 100%, 40%);">+    def newval(inmap, key, val):</span><br><span style="color: hsl(120, 100%, 40%);">+        if key in inmap:</span><br><span style="color: hsl(120, 100%, 40%);">+            return inmap[key](val)</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return val</span><br><span style="color: hsl(120, 100%, 40%);">+    return {d[0]: newval(inmap, d[0], d[1]) for d in indata.items()}</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%);">+# ETSI TS 102 221 Section 11.1.1.3</span><br><span style="color: hsl(120, 100%, 40%);">+def decode_select_response(resp_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+    fixup_fcp_proprietary_tlv_map(FCP_Proprietary_TLV_MAP)</span><br><span style="color: hsl(120, 100%, 40%);">+    resp_hex = resp_hex.upper()</span><br><span style="color: hsl(120, 100%, 40%);">+    # outer layer</span><br><span style="color: hsl(120, 100%, 40%);">+    fcp_base_tlv = TLV(['62'])</span><br><span style="color: hsl(120, 100%, 40%);">+    fcp_base = fcp_base_tlv.parse(resp_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+    # actual FCP</span><br><span style="color: hsl(120, 100%, 40%);">+    fcp_tlv = TLV(FCP_TLV_MAP)</span><br><span style="color: hsl(120, 100%, 40%);">+    fcp = fcp_tlv.parse(fcp_base['62'])</span><br><span style="color: hsl(120, 100%, 40%);">+    # further decode the proprietary information</span><br><span style="color: hsl(120, 100%, 40%);">+    if fcp['A5']:</span><br><span style="color: hsl(120, 100%, 40%);">+        prop_tlv = TLV(FCP_Proprietary_TLV_MAP)</span><br><span style="color: hsl(120, 100%, 40%);">+        prop = prop_tlv.parse(fcp['A5'])</span><br><span style="color: hsl(120, 100%, 40%);">+        fcp['A5'] = tlv_val_interpret(FCP_prorietary_interpreter_map, prop)</span><br><span style="color: hsl(120, 100%, 40%);">+        fcp['A5'] = tlv_key_replace(FCP_Proprietary_TLV_MAP, fcp['A5'])</span><br><span style="color: hsl(120, 100%, 40%);">+    # finally make sure we get human-readable keys in the output dict</span><br><span style="color: hsl(120, 100%, 40%);">+    r = tlv_val_interpret(FCP_interpreter_map, fcp)</span><br><span style="color: hsl(120, 100%, 40%);">+    return tlv_key_replace(FCP_TLV_MAP, r)</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%);">+# TS 102 221 Section 13.1</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_DIR(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='2f00', sfid=0x1e, name='EF.DIR', desc='Application Directory'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={5,54})</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_record_hex(self, raw_hex_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        raw_hex_data = raw_hex_data.upper()</span><br><span style="color: hsl(120, 100%, 40%);">+        atempl_base_tlv = TLV(['61'])</span><br><span style="color: hsl(120, 100%, 40%);">+        atempl_base = atempl_base_tlv.parse(raw_hex_data)</span><br><span style="color: hsl(120, 100%, 40%);">+        atempl_TLV_MAP = {'4F': 'aid_value', 50:'label'}</span><br><span style="color: hsl(120, 100%, 40%);">+        atempl_tlv = TLV(atempl_TLV_MAP)</span><br><span style="color: hsl(120, 100%, 40%);">+        atempl = atempl_tlv.parse(atempl_base['61'])</span><br><span style="color: hsl(120, 100%, 40%);">+        # FIXME: "All other Dos are according to ISO/IEC 7816-4"</span><br><span style="color: hsl(120, 100%, 40%);">+        return tlv_key_replace(atempl_TLV_MAP, atempl)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 102 221 Section 13.2</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_ICCID(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='2fe2', sfid=0x02, name='EF.ICCID', desc='ICC Identification'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, size={10,10})</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_hex(self, raw_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'iccid': dec_iccid(raw_hex)}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_hex(self, abstract):</span><br><span style="color: hsl(120, 100%, 40%);">+        return enc_iccid(abstract['iccid'])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 102 221 Section 13.3</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_PL(TransRecEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='2f05', sfid=0x05, name='EF.PL', desc='Preferred Languages'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=2, size={2,None})</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 102 221 Section 13.4</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_ARR(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='2f06', sfid=0x06, name='EF.ARR', desc='Access Rule Reference'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 102 221 Section 13.6</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_UMPC(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='2f08', sfid=0x08, name='EF.UMPC', desc='UICC Maximum Power Consumption'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, size={5,5})</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 CardProfileUICC(CardProfile):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        files = [</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_DIR(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_ICCID(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_PL(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_ARR(),</span><br><span style="color: hsl(120, 100%, 40%);">+            # FIXME: DF.CD</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_UMPC(),</span><br><span style="color: hsl(120, 100%, 40%);">+        ]</span><br><span style="color: hsl(120, 100%, 40%);">+        sw = {</span><br><span style="color: hsl(120, 100%, 40%);">+          'Normal': {</span><br><span style="color: hsl(120, 100%, 40%);">+            '9000': 'Normal ending of the command',</span><br><span style="color: hsl(120, 100%, 40%);">+            '91xx': 'Normal ending of the command, with extra information from the proactive UICC containing a command for the terminal',</span><br><span style="color: hsl(120, 100%, 40%);">+            '92xx': 'Normal ending of the command, with extra information concerning an ongoing data transfer session',</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+          'Postponed processing': {</span><br><span style="color: hsl(120, 100%, 40%);">+            '9300': 'SIM Application Toolkit is busy. Command cannot be executed at present, further normal commands are allowed',</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+          'Warnings': {</span><br><span style="color: hsl(120, 100%, 40%);">+            '6200': 'No information given, state of non-volatile memory unchanged',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6281': 'Part of returned data may be corrupted',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6282': 'End of file/record reached before reading Le bytes or unsuccessful search',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6283': 'Selected file invalidated',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6284': 'Selected file in termination state',</span><br><span style="color: hsl(120, 100%, 40%);">+            '62f1': 'More data available',</span><br><span style="color: hsl(120, 100%, 40%);">+            '62f2': 'More data available and proactive command pending',</span><br><span style="color: hsl(120, 100%, 40%);">+            '62f3': 'Response data available',</span><br><span style="color: hsl(120, 100%, 40%);">+            '63f1': 'More data expected',</span><br><span style="color: hsl(120, 100%, 40%);">+            '63f2': 'More data expected and proactive command pending',</span><br><span style="color: hsl(120, 100%, 40%);">+            '63cx': 'Command successful but after using an internal update retry routine X times',</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+          'Execution errors': {</span><br><span style="color: hsl(120, 100%, 40%);">+            '6400': 'No information given, state of non-volatile memory unchanged',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6500': 'No information given, state of non-volatile memory changed',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6581': 'Memory problem',</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+          'Checking errors': {</span><br><span style="color: hsl(120, 100%, 40%);">+            '6700': 'Wrong length',</span><br><span style="color: hsl(120, 100%, 40%);">+            '67xx': 'The interpretation of this status word is command dependent',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6b00': 'Wrong parameter(s) P1-P2',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6d00': 'Instruction code not supported or invalid',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6e00': 'Class not supported',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6f00': 'Technical problem, no precise diagnosis',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6fxx': 'The interpretation of this status word is command dependent',</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+          'Functions in CLA not supported': {</span><br><span style="color: hsl(120, 100%, 40%);">+            '6800': 'No information given',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6881': 'Logical channel not supported',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6882': 'Secure messaging not supported',</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+          'Command not allowed': {</span><br><span style="color: hsl(120, 100%, 40%);">+            '6900': 'No information given',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6981': 'Command incompatible with file structure',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6982': 'Security status not satisfied',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6983': 'Authentication/PIN method blocked',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6984': 'Referenced data invalidated',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6985': 'Conditions of use not satisfied',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6986': 'Command not allowed (no EF selected)',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6989': 'Command not allowed - secure channel - security not satisfied',</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+          'Wrong parameters': {</span><br><span style="color: hsl(120, 100%, 40%);">+            '6a80': 'Incorrect parameters in the data field',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6a81': 'Function not supported',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6a82': 'File not found',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6a83': 'Record not found',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6a84': 'Not enough memory space',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6a86': 'Incorrect parameters P1 to P2',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6a87': 'Lc inconsistent with P1 to P2',</span><br><span style="color: hsl(120, 100%, 40%);">+            '6a88': 'Referenced data not found',</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+          'Application errors': {</span><br><span style="color: hsl(120, 100%, 40%);">+            '9850': 'INCREASE cannot be performed, max value reached',</span><br><span style="color: hsl(120, 100%, 40%);">+            '9862': 'Authentication error, application specific',</span><br><span style="color: hsl(120, 100%, 40%);">+            '9863': 'Security session or association expired',</span><br><span style="color: hsl(120, 100%, 40%);">+            '9864': 'Minimum UICC suspension time is too long',</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%);">+        super().__init__('UICC', 'ETSI TS 102 221', files, sw)</span><br><span>diff --git a/pySim/ts_31_102.py b/pySim/ts_31_102.py</span><br><span>index e7f27b0..02b0aea 100644</span><br><span>--- a/pySim/ts_31_102.py</span><br><span>+++ b/pySim/ts_31_102.py</span><br><span>@@ -263,3 +263,134 @@</span><br><span>        'ePDGIdEm': '6FF5',</span><br><span>  'ePDGSelectionEm': '6FF6',</span><br><span> }</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%);">+# ADF.USIM</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 pySim.filesystem import *</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_51_011 import EF_IMSI, EF_xPLMNwAcT, EF_SPN, EF_CBMI, EF_ACC, EF_PLMNsel, EF_AD</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_51_011 import EF_CBMID, EF_ECC, EF_CBMIR</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import pySim.ts_102_221</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_LI(TransRecEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f05', sfid=None, name='EF.LI', size={2,None}, rec_len=2,</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc='Language Indication'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_record_bin(self, in_bin):</span><br><span style="color: hsl(120, 100%, 40%);">+        if in_bin == b'\xff\xff':</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            # officially this is 7-bit GSM alphabet with one padding bit in each byte</span><br><span style="color: hsl(120, 100%, 40%);">+            return in_bin.decode('ascii')</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_record_bin(self, in_json):</span><br><span style="color: hsl(120, 100%, 40%);">+        if in_json == None:</span><br><span style="color: hsl(120, 100%, 40%);">+            return b'\xff\xff'</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            # officially this is 7-bit GSM alphabet with one padding bit in each byte</span><br><span style="color: hsl(120, 100%, 40%);">+            return in_json.encode('ascii')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_Keys(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f08', sfid=0x08, name='EF.Keys', size={33,33},</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc='Ciphering and Integrity Keys'):</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%);">+    def _decode_bin(self, in_bin):</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'ksi': in_bin[0],</span><br><span style="color: hsl(120, 100%, 40%);">+                'ck': b2h(in_bin[1:17]),</span><br><span style="color: hsl(120, 100%, 40%);">+                'ik': b2h(in_bin[17:33])}</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(in_json['ksi']) + h2b(in_json['ck']) + h2b(in_json['ik'])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.7</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_UST(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f38', sfid=0x04, name='EF.UST', desc='USIM Service Table'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size={1,17})</span><br><span style="color: hsl(120, 100%, 40%);">+        # add those commands to the general commands of a TransparentEF</span><br><span style="color: hsl(120, 100%, 40%);">+        self.shell_commands += [self.AddlShellCommands()]</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_bin(self, in_bin):</span><br><span style="color: hsl(120, 100%, 40%);">+        ret = []</span><br><span style="color: hsl(120, 100%, 40%);">+        for i in range (0, len(in_bin)):</span><br><span style="color: hsl(120, 100%, 40%);">+            byte = in_bin[i]</span><br><span style="color: hsl(120, 100%, 40%);">+            for bitno in range(0,7):</span><br><span style="color: hsl(120, 100%, 40%);">+                if byte & (1 << bitno):</span><br><span style="color: hsl(120, 100%, 40%);">+                    ret.append(i * 8 + bitno + 1)</span><br><span style="color: hsl(120, 100%, 40%);">+        return ret</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_bin(self, in_json):</span><br><span style="color: hsl(120, 100%, 40%);">+        # FIXME: size this to length of file</span><br><span style="color: hsl(120, 100%, 40%);">+        ret = bytearray(20)</span><br><span style="color: hsl(120, 100%, 40%);">+        for srv in in_json:</span><br><span style="color: hsl(120, 100%, 40%);">+            print("srv=%d"%srv)</span><br><span style="color: hsl(120, 100%, 40%);">+            srv = srv-1</span><br><span style="color: hsl(120, 100%, 40%);">+            byte_nr = srv // 8</span><br><span style="color: hsl(120, 100%, 40%);">+            # FIXME: detect if service out of range was selected</span><br><span style="color: hsl(120, 100%, 40%);">+            bit_nr = srv % 8</span><br><span style="color: hsl(120, 100%, 40%);">+            ret[byte_nr] |= (1 << bit_nr)</span><br><span style="color: hsl(120, 100%, 40%);">+        return ret</span><br><span style="color: hsl(120, 100%, 40%);">+    @with_default_category('File-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_ust_service_activate(self, arg):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Activate a service within EF.UST"""</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.card.update_ust(int(arg), 1)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_ust_service_deactivate(self, arg):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Deactivate a service within EF.UST"""</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.card.update_ust(int(arg), 0)</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 ADF_USIM(CardADF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, aid='a0000000871002', name='ADF.USIM', fid=None, sfid=None,</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc='USIM 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.ShellCommands()]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        files = [</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_LI(sfid=0x02),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_IMSI(sfid=0x07),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_Keys(),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_Keys('6f09', 0x09, 'EF.KeysPS', desc='Ciphering and Integrity Keys for PS domain'),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_xPLMNwAcT('6f60', 0x0a, 'EF.PLMNwAcT',</span><br><span style="color: hsl(120, 100%, 40%);">+                       'User controlled PLMN Selector with Access Technology'),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f31', 0x12, 'EF.HPPLMN', 'Higher Priority PLMN search period'),</span><br><span style="color: hsl(120, 100%, 40%);">+          # EF.ACMmax</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_UST(),</span><br><span style="color: hsl(120, 100%, 40%);">+          CyclicEF('6f39', None, 'EF.ACM', 'Accumulated call meter', rec_len={3,3}),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f3e', None, 'EF.GID1', 'Group Identifier Level 1'),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f3f', None, 'EF.GID2', 'Group Identifier Level 2'),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_SPN(),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f41', None, 'EF.PUCT', 'Price per unit and currency table', size={5,5}),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_CBMI(),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_ACC(sfid=0x06),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_PLMNsel('6f7b', 0x0d, 'EF.FPLMN', 'Forbidden PLMNs', size={12,None}),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f7e', 0x0b, 'EF.LOCI', 'Locationn information', size={11,11}),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_AD(sfid=0x03),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_CBMID(sfid=0x0e),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_ECC(sfid=0x01),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_CBMIR(),</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 pySim.ts_102_221.decode_select_response(data_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @with_default_category('File-Specific Commands')</span><br><span style="color: hsl(120, 100%, 40%);">+    class ShellCommands(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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.102 Section 7.3</span><br><span style="color: hsl(120, 100%, 40%);">+sw_usim = {</span><br><span style="color: hsl(120, 100%, 40%);">+    'Security management': {</span><br><span style="color: hsl(120, 100%, 40%);">+        '9862': 'Authentication error, incorrect MAC',</span><br><span style="color: hsl(120, 100%, 40%);">+        '9864': 'Authentication error, security context not supported',</span><br><span style="color: hsl(120, 100%, 40%);">+        '9865': 'Key freshness failure',</span><br><span style="color: hsl(120, 100%, 40%);">+        '9866': 'Authentication error, no memory space available',</span><br><span style="color: hsl(120, 100%, 40%);">+        '9867': 'Authentication error, no memory space available in EF MUK',</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%);">+CardApplicationUSIM = CardApplication('USIM', adf=ADF_USIM(), sw=sw_usim)</span><br><span>diff --git a/pySim/ts_31_103.py b/pySim/ts_31_103.py</span><br><span>index d9b771d..0b0a4f1 100644</span><br><span>--- a/pySim/ts_31_103.py</span><br><span>+++ b/pySim/ts_31_103.py</span><br><span>@@ -6,6 +6,7 @@</span><br><span> </span><br><span> #</span><br><span> # Copyright (C) 2020 Supreeth Herle <herlesupreeth@gmail.com></span><br><span style="color: hsl(120, 100%, 40%);">+# Copyright (C) 2021 Harald Welte <laforge@osmocom.org></span><br><span> #</span><br><span> # This program is free software: you can redistribute it and/or modify</span><br><span> # it under the terms of the GNU General Public License as published by</span><br><span>@@ -21,6 +22,11 @@</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(120, 100%, 40%);">+from pySim.filesystem import *</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.utils import *</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_51_011 import EF_AD</span><br><span style="color: hsl(120, 100%, 40%);">+import pySim.ts_102_221</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Mapping between ISIM Service Number and its description</span><br><span> EF_IST_map = {</span><br><span>    1: 'P-CSCF address',</span><br><span>@@ -66,3 +72,130 @@</span><br><span>   'XCAPConfigData': '6FFC',</span><br><span>    'WebRTCURI': '6FFA'</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.2</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_IMPI(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f02', sfid=0x02, name='EF.IMPI', desc='IMS private user identity'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.3</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_DOMAIN(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f05', sfid=0x05, name='EF.DOMAIN', desc='Home Network Domain Name'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.4</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_IMPU(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f04', sfid=0x04, name='EF.IMPU', desc='IMS public user identity'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.6</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_ARR(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f06', sfid=0x06, name='EF.ARR', desc='Access Rule Reference'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.7</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_IST(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f07', sfid=0x07, name='EF.IST', desc='ISIM Service Table'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size={1,4})</span><br><span style="color: hsl(120, 100%, 40%);">+        # add those commands to the general commands of a TransparentEF</span><br><span style="color: hsl(120, 100%, 40%);">+        self.shell_commands += [self.AddlShellCommands()]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @with_default_category('File-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_ist_service_activate(self, arg):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Activate a service within EF.IST"""</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.card.update_ist(int(arg), 1)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_ist_service_deactivate(self, arg):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Deactivate a service within EF.IST"""</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.card.update_ist(int(arg), 0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.8</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_PCSCF(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f09', sfid=None, name='EF.P-CSCF', desc='P-CSCF Address'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_record_hex(self, raw_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+        # FIXME: this doesn't do JSON output</span><br><span style="color: hsl(120, 100%, 40%);">+        return dec_addr_tlv(raw_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_record_hex(self, json_in):</span><br><span style="color: hsl(120, 100%, 40%);">+        return enc_addr_tlv(json_in)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.9</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_GBABP(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6fd5', sfid=None, name='EF.GBABP', desc='GBA Bootstrappng'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.10</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_GBANL(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6fd7', sfid=None, name='EF.GBANL', desc='GBA NAF List'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.11</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_NAFKCA(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6fdd', sfid=None, name='EF.NAFKCA', desc='NAF Key Centre Address'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.16</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_UICCIARI(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6fe7', sfid=None, name='EF.UICCIARI', desc='UICC IARI'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.18</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_IMSConfigData(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6ff8', sfid=None, name='EF.IMSConfigData', desc='IMS Configuration Data'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.19</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_XCAPConfigData(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6ffc', sfid=None, name='EF.XCAPConfigData', desc='XCAP Configuration Data'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 4.2.20</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_WebRTCURI(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6ffa', sfid=None, name='EF.WebRTCURI', desc='WebRTC URI'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)</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 ADF_ISIM(CardADF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, aid='a0000000871004', name='ADF.ISIM', fid=None, sfid=None,</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc='ISIM 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        files = [</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_IMPI(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_DOMAIN(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_IMPU(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_AD(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_ARR(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_IST(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_PCSCF(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_GBABP(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_GBANL(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_NAFKCA(),</span><br><span style="color: hsl(120, 100%, 40%);">+            # SMS</span><br><span style="color: hsl(120, 100%, 40%);">+            # SMSS</span><br><span style="color: hsl(120, 100%, 40%);">+            # SMSR</span><br><span style="color: hsl(120, 100%, 40%);">+            #EF_SMSP(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_UICCIARI(),</span><br><span style="color: hsl(120, 100%, 40%);">+            # FromPreferred</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_IMSConfigData(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_XCAPConfigData(),</span><br><span style="color: hsl(120, 100%, 40%);">+            EF_WebRTCURI(),</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 pySim.ts_102_221.decode_select_response(data_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.103 Section 7.1</span><br><span style="color: hsl(120, 100%, 40%);">+sw_isim = {</span><br><span style="color: hsl(120, 100%, 40%);">+    'Security management': {</span><br><span style="color: hsl(120, 100%, 40%);">+        '9862': 'Authentication error, incorrect MAC',</span><br><span style="color: hsl(120, 100%, 40%);">+        '9864': 'Authentication error, security context not supported',</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%);">+CardApplicationISIM = CardApplication('ISIM', adf=ADF_ISIM(), sw=sw_isim)</span><br><span>diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py</span><br><span>index ef40ba1..03d74ad 100644</span><br><span>--- a/pySim/ts_51_011.py</span><br><span>+++ b/pySim/ts_51_011.py</span><br><span>@@ -1,10 +1,15 @@</span><br><span> # -*- coding: utf-8 -*-</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-""" Various constants from ETSI TS 151.011</span><br><span style="color: hsl(120, 100%, 40%);">+""" Various constants from ETSI TS 151.011 +</span><br><span style="color: hsl(120, 100%, 40%);">+Representation of the GSM SIM/USIM/ISIM filesystem hierarchy.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The File (and its derived classes) uses the classes of pySim.filesystem in</span><br><span style="color: hsl(120, 100%, 40%);">+order to describe the files specified in the relevant ETSI + 3GPP specifications.</span><br><span> """</span><br><span> </span><br><span> #</span><br><span> # Copyright (C) 2017 Alexander.Chemeris <Alexander.Chemeris@gmail.com></span><br><span style="color: hsl(120, 100%, 40%);">+# Copyright (C) 2021 Harald Welte <laforge@osmocom.org></span><br><span> #</span><br><span> # This program is free software: you can redistribute it and/or modify</span><br><span> # it under the terms of the GNU General Public License as published by</span><br><span>@@ -323,3 +328,301 @@</span><br><span>     '02' : 'maintenance (off line)',</span><br><span>     '04' : 'cell test operation',</span><br><span> }</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 pySim.utils import *</span><br><span style="color: hsl(120, 100%, 40%);">+from struct import pack, unpack</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.filesystem import *</span><br><span style="color: hsl(120, 100%, 40%);">+import pySim.ts_102_221</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%);">+# DF.TELECOM</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%);">+# TS 51.011 Section 10.5.1</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_ADN(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f3a', sfid=None, name='EF.ADN', desc='Abbreviated Dialing Numbers'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={14, 30})</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_record_bin(self, raw_bin_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        alpha_id_len = len(raw_bin_data) - 14</span><br><span style="color: hsl(120, 100%, 40%);">+        alpha_id = raw_bin_data[:alpha_id_len]</span><br><span style="color: hsl(120, 100%, 40%);">+        u = unpack('!BB10sBB', raw_bin_data[-14:])</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'alpha_id': alpha_id, 'len_of_bcd': u[0], 'ton_npi': u[1],</span><br><span style="color: hsl(120, 100%, 40%);">+                'dialing_nr': u[2], 'cap_conf_id': u[3], 'ext1_record_id': u[4]}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.5.5</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_MSISDN(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f4f', sfid=None, name='EF.MSISDN', desc='MSISDN'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={15, None})</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_record_hex(self, raw_hex_data):</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'msisdn': dec_msisdn(raw_hex_data)}</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_record_hex(self, abstract):</span><br><span style="color: hsl(120, 100%, 40%);">+        return enc_msisdn(abstract['msisdn'])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.5.6</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_SMSP(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f42', sfid=None, name='EF.SMSP', desc='Short message service parameters'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={28, None})</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class DF_TELECOM(CardDF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='7f10', name='DF.TELECOM', desc=None):</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%);">+          EF_ADN(),</span><br><span style="color: hsl(120, 100%, 40%);">+          # FDN, SMS, CCP, ECCP</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_MSISDN(),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_SMSP(),</span><br><span style="color: hsl(120, 100%, 40%);">+          # SMSS, LND, SDN, EXT1, EXT2, EXT3, BDN, EXT4, SMSR, CMI</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 decode_select_response(data_hex)</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%);">+# DF.GSM</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%);">+# TS 51.011 Section 10.3.1</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_LP(TransRecEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f05', sfid=None, name='EF.LP', size={1,None}, rec_len=1,</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc='Language Preference'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_record_bin(self, in_bin):</span><br><span style="color: hsl(120, 100%, 40%);">+        return b2h(in_bin)</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_record_bin(self, in_json):</span><br><span style="color: hsl(120, 100%, 40%);">+        return h2b(in_json)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.2</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_IMSI(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f07', sfid=None, name='EF.IMSI', desc='IMSI', size={9,9}):</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%);">+    def _decode_hex(self, raw_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'imsi': dec_imsi(raw_hex)}</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_hex(self, abstract):</span><br><span style="color: hsl(120, 100%, 40%);">+        return enc_imsi(abstract['imsi'])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.4</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_PLMNsel(TransRecEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f30', sfid=None, name='EF.PLMNsel', desc='PLMN selector',</span><br><span style="color: hsl(120, 100%, 40%);">+                 size={24,None}, rec_len=3):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, name=name, sfid=sfid, desc=desc, size=size, rec_len=rec_len)</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_record_hex(self, in_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+        if in_hex[:6] == "ffffff":</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return dec_plmn(in_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_record_hex(self, in_json):</span><br><span style="color: hsl(120, 100%, 40%);">+        if in_json == None:</span><br><span style="color: hsl(120, 100%, 40%);">+            return "ffffff"</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return enc_plmn(in_json['mcc'], in_json['mnc'])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.7</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_ServiceTable(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid, sfid, name, desc, size, table):</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%);">+        self.table = table</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_bin(self, raw_bin):</span><br><span style="color: hsl(120, 100%, 40%);">+        ret = {}</span><br><span style="color: hsl(120, 100%, 40%);">+        for i in range(0, len(raw_bin)*4):</span><br><span style="color: hsl(120, 100%, 40%);">+            service_nr = i+1</span><br><span style="color: hsl(120, 100%, 40%);">+            byte = int(raw_bin[i//4])</span><br><span style="color: hsl(120, 100%, 40%);">+            bit_offset = (i % 4) * 2</span><br><span style="color: hsl(120, 100%, 40%);">+            bits = (byte >> bit_offset) & 3</span><br><span style="color: hsl(120, 100%, 40%);">+            ret[service_nr] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                     'description': self.table[service_nr] or None,</span><br><span style="color: hsl(120, 100%, 40%);">+                     'allocated': True if bits & 1 else False,</span><br><span style="color: hsl(120, 100%, 40%);">+                     'activated': True if bits & 2 else False,</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+        return ret</span><br><span style="color: hsl(120, 100%, 40%);">+    # TODO: encoder</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.11</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_SPN(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f46', sfid=None, name='EF.SPN', desc='Service Provider Name', size={17,17}):</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%);">+    def _decode_hex(self, raw_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'spn': dec_spn(raw_hex)}</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_hex(self, abstract):</span><br><span style="color: hsl(120, 100%, 40%);">+        return enc_spn(abstract['spn'])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.13</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_CBMI(TransRecEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f45', sfid=None, name='EF.CBMI', size={2,None}, rec_len=2,</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc='Cell Broadcast message identifier selection'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.15</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_ACC(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f78', sfid=None, name='EF.ACC', desc='Access Control Class', size={2,2}):</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%);">+    def _decode_bin(self, raw_bin):</span><br><span style="color: hsl(120, 100%, 40%);">+        return {'acc': unpack('!H', raw_bin)[0]}</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_bin(self, abstract):</span><br><span style="color: hsl(120, 100%, 40%);">+        return pack('!H', abstract['acc'])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.18</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_AD(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    OP_MODE = {</span><br><span style="color: hsl(120, 100%, 40%);">+            0x00: 'normal operation',</span><br><span style="color: hsl(120, 100%, 40%);">+            0x80: 'type approval operations',</span><br><span style="color: hsl(120, 100%, 40%);">+            0x01: 'normal operation + specific facilities',</span><br><span style="color: hsl(120, 100%, 40%);">+            0x81: 'type approval + specific facilities',</span><br><span style="color: hsl(120, 100%, 40%);">+            0x02: 'maintenance (off line)',</span><br><span style="color: hsl(120, 100%, 40%);">+            0x04: 'cell test operation',</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6fad', sfid=None, name='EF.AD', desc='Administrative Data', size={3,4}):</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%);">+    def _decode_bin(self, raw_bin):</span><br><span style="color: hsl(120, 100%, 40%);">+        u = unpack('!BH', raw_bin[:3])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.13</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_CBMID(EF_CBMI):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f48', sfid=None, name='EF.CBMID', size={2,None}, rec_len=2,</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc='Cell Broadcast Message Identifier for Data Download'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.26</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_ECC(LinFixedEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6fb7', sfid=None, name='EF.ECC', desc='Emergency Call Codes'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={4, 20})</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 51.011 Section 10.3.28</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_CBMIR(TransRecEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='6f50', sfid=None, name='EF.CBMIR', size={4,None}, rec_len=4,</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc='Cell Broadcast message identifier range selection'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)</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%);">+# TS 51.011 Section 10.3.35..37</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_xPLMNwAcT(TransRecEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid, sfid=None, name=None, desc=None, size={40,None}, rec_len=5):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_record_hex(self, in_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+        if in_hex[:6] == "ffffff":</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return dec_xplmn_w_act(in_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_record_hex(self, in_json):</span><br><span style="color: hsl(120, 100%, 40%);">+        if in_json == None:</span><br><span style="color: hsl(120, 100%, 40%);">+            return "ffffff0000"</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            hplmn = enc_plmn(in_json['mcc'], in_json['mnc'])</span><br><span style="color: hsl(120, 100%, 40%);">+            act = self.enc_act(in_json['act'])</span><br><span style="color: hsl(120, 100%, 40%);">+            return hplmn + act</span><br><span style="color: hsl(120, 100%, 40%);">+    @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+    def enc_act(in_list):</span><br><span style="color: hsl(120, 100%, 40%);">+        u16 = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        # first the simple ones</span><br><span style="color: hsl(120, 100%, 40%);">+        if 'UTRAN' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x8000</span><br><span style="color: hsl(120, 100%, 40%);">+        if 'NG-RAN' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x0800</span><br><span style="color: hsl(120, 100%, 40%);">+        if 'GSM COMPACT' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x0040</span><br><span style="color: hsl(120, 100%, 40%);">+        if 'cdma2000 HRPD' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x0020</span><br><span style="color: hsl(120, 100%, 40%);">+        if 'cdma2000 1xRTT' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x0010</span><br><span style="color: hsl(120, 100%, 40%);">+        # E-UTRAN</span><br><span style="color: hsl(120, 100%, 40%);">+        if 'E-UTRAN WB-S1' and 'E-UTRAN NB-S1' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x7000   # WB-S1 and NB-S1</span><br><span style="color: hsl(120, 100%, 40%);">+        elif 'E-UTRAN NB-S1' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x6000   # only WB-S1</span><br><span style="color: hsl(120, 100%, 40%);">+        elif 'E-UTRAN NB-S1' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x5000   # only NB-S1</span><br><span style="color: hsl(120, 100%, 40%);">+        # GSM mess</span><br><span style="color: hsl(120, 100%, 40%);">+        if 'GSM' in in_list and 'EC-GSM-IoT' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x008C</span><br><span style="color: hsl(120, 100%, 40%);">+        elif 'GSM' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x0084</span><br><span style="color: hsl(120, 100%, 40%);">+        elif 'EC-GSM-IuT' in in_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            u16 |= 0x0088</span><br><span style="color: hsl(120, 100%, 40%);">+        return '%04X'%(u16)</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 DF_GSM(CardDF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='7f20', name='DF.GSM', desc='GSM Network 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%);">+          EF_LP(),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_IMSI(),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('5f20', None, 'EF.Kc', 'Ciphering key Kc'),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_PLMNsel(),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f31', None, 'EF.HPPLMN', 'Higher Priority PLMN search period'),</span><br><span style="color: hsl(120, 100%, 40%);">+          # ACMmax</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_ServiceTable('6f37', None, 'EF.SST', 'SIM service table', table=EF_SST_map, size={2,16}),</span><br><span style="color: hsl(120, 100%, 40%);">+          CyclicEF('6f39', None, 'EF.ACM', 'Accumulated call meter', rec_len={4,3}),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f3e', None, 'EF.GID1', 'Group Identifier Level 1'),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f3f', None, 'EF.GID2', 'Group Identifier Level 2'),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_SPN(),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f41', None, 'EF.PUCT', 'Price per unit and currency table', size={5,5}),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_CBMI(),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f7f', None, 'EF.BCCH', 'Broadcast control channels', size={16,16}),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_ACC(),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_PLMNsel('6f7b', None, 'EF.FPLMN', 'Forbidden PLMNs', size={12,12}),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6f7e', None, 'EF.LOCI', 'Locationn information', size={11,11}),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_AD(),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('6fa3', None, 'EF.Phase', 'Phase identification', size={1,1}),</span><br><span style="color: hsl(120, 100%, 40%);">+        # TODO EF.VGCS VGCSS, VBS, VBSS, eMLPP, AAeM</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_CBMID(),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_ECC(),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_CBMIR(),</span><br><span style="color: hsl(120, 100%, 40%);">+          # DCK, CNL, NIA, KcGRS, LOCIGPRS, SUME</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_xPLMNwAcT('6f60', None, 'EF.PLMNwAcT',</span><br><span style="color: hsl(120, 100%, 40%);">+                                   'User controlled PLMN Selector with Access Technology'),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_xPLMNwAcT('6f61', None, 'EF.OPLMNwAcT',</span><br><span style="color: hsl(120, 100%, 40%);">+                                   'Operator controlled PLMN Selector with Access Technology'),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_xPLMNwAcT('6f62', None, 'EF.HPLMNwAcT', 'HPLMN Selector with Access Technology'),</span><br><span style="color: hsl(120, 100%, 40%);">+          # CPBCCH, InvScan, PNN, OPL, MBDN, MBI, MWIS, CFIS, EXT5, EXT6, EXT7, SPDI, MMSN, EXT8</span><br><span style="color: hsl(120, 100%, 40%);">+          # MMSICP, MMSUP, MMSUCP</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 decode_select_response(data_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def decode_select_response(resp_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+    resp_bin = h2b(resp_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+    if resp_bin[0] == 0x62:</span><br><span style="color: hsl(120, 100%, 40%);">+        return pySim.ts_102_221.decode_select_response(resp_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+    struct_of_file_map = {</span><br><span style="color: hsl(120, 100%, 40%);">+        0: 'transparent',</span><br><span style="color: hsl(120, 100%, 40%);">+        1: 'linear_fixed',</span><br><span style="color: hsl(120, 100%, 40%);">+        3: 'cyclic'</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+    type_of_file_map = {</span><br><span style="color: hsl(120, 100%, 40%);">+        1: 'mf',</span><br><span style="color: hsl(120, 100%, 40%);">+        2: 'df',</span><br><span style="color: hsl(120, 100%, 40%);">+        4: 'working_ef'</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+    ret = {</span><br><span style="color: hsl(120, 100%, 40%);">+        'file_descriptor': {},</span><br><span style="color: hsl(120, 100%, 40%);">+        'proprietary_info': {},</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+    ret['file_id'] = b2h(resp_bin[4:6])</span><br><span style="color: hsl(120, 100%, 40%);">+    ret['proprietary_info']['available_memory'] = int.from_bytes(resp_bin[2:4], 'big')</span><br><span style="color: hsl(120, 100%, 40%);">+    file_type = type_of_file_map[resp_bin[6]] if resp_bin[6] in type_of_file_map else resp_bin[6]</span><br><span style="color: hsl(120, 100%, 40%);">+    ret['file_descriptor']['file_type'] = file_type</span><br><span style="color: hsl(120, 100%, 40%);">+    if file_type in ['mf', 'df']:</span><br><span style="color: hsl(120, 100%, 40%);">+        ret['file_characteristics'] = b2h(resp_bin[13])</span><br><span style="color: hsl(120, 100%, 40%);">+        ret['num_direct_child_df'] = int(resp_bin[14], 16)</span><br><span style="color: hsl(120, 100%, 40%);">+        ret['num_direct_child_ef'] = int(resp_bin[15], 16)</span><br><span style="color: hsl(120, 100%, 40%);">+        ret['num_chv_unbkock_adm_codes'] = int(resp_bin[16])</span><br><span style="color: hsl(120, 100%, 40%);">+        # CHV / UNBLOCK CHV stats</span><br><span style="color: hsl(120, 100%, 40%);">+    elif file_type in ['working_ef']:</span><br><span style="color: hsl(120, 100%, 40%);">+        file_struct = struct_of_file_map[resp_bin[13]] if resp_bin[13] in struct_of_file_map else resp_bin[13]</span><br><span style="color: hsl(120, 100%, 40%);">+        ret['file_descriptor']['structure'] = file_struct</span><br><span style="color: hsl(120, 100%, 40%);">+        ret['access_conditions'] = b2h(resp_bin[8:10])</span><br><span style="color: hsl(120, 100%, 40%);">+        if resp_bin[11] & 0x01 == 0:</span><br><span style="color: hsl(120, 100%, 40%);">+            ret['life_cycle_status_int'] = 'operational_activated'</span><br><span style="color: hsl(120, 100%, 40%);">+        elif resp_bin[11] & 0x04:</span><br><span style="color: hsl(120, 100%, 40%);">+            ret['life_cycle_status_int'] = 'operational_deactivated'</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            ret['life_cycle_status_int'] = 'terminated'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return ret</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+CardProfileSIM = CardProfile('SIM', desc='GSM SIM Card', files_in_mf=[DF_TELECOM(), DF_GSM()])</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/pysim/+/23175">change 23175</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/+/23175"/><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: Iad117596e922223bdc1e5b956f84844b7c577e02 </div>
<div style="display:none"> Gerrit-Change-Number: 23175 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </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-MessageType: merged </div>