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

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">pySim-shell: complete CHV/PIN management tools<br><br>At the moment we only have a basic version of a verify_chv commnad, but<br>in order to handle any CHV/PIN related situation we also need commands<br>to enable, disable, change and unblock CHV.<br><br>- fix verify_chv commnad: more distinct parameter names, better help<br>  strings, correct pin code encoding and add external source lookup<br>- Add unblock_chv, change_chv, enable_chv and disable_chv commands<br>- add/fix related functions in commands.py<br><br>Change-Id: Ic89446e6bd2021095e579fb6b20458df48ba6413<br>Related: OS#4963<br>---<br>M pySim-shell.py<br>M pySim/card_data.py<br>M pySim/commands.py<br>3 files changed, 108 insertions(+), 13 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/pySim-shell.py b/pySim-shell.py</span><br><span>index 68896cb..11953eb 100755</span><br><span>--- a/pySim-shell.py</span><br><span>+++ b/pySim-shell.py</span><br><span>@@ -39,7 +39,7 @@</span><br><span> from pySim.commands import SimCardCommands</span><br><span> from pySim.cards import card_detect, Card</span><br><span> from pySim.utils import h2b, swap_nibbles, rpad, h2s</span><br><span style="color: hsl(0, 100%, 40%);">-from pySim.utils import dec_st, init_reader, sanitize_pin_adm, tabulate_str_list</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.utils import dec_st, init_reader, sanitize_pin_adm, tabulate_str_list, is_hex</span><br><span> from pySim.card_handler import card_handler</span><br><span> </span><br><span> from pySim.filesystem import CardMF, RuntimeState, CardDF, CardADF</span><br><span>@@ -141,15 +141,79 @@</span><br><span>                 index_dict = { 1: self._cmd.rs.selected_file.get_selectable_names() }</span><br><span>                return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+  def get_code(self, code):</span><br><span style="color: hsl(120, 100%, 40%);">+             """Use code either directly or try to get it from external data source"""</span><br><span style="color: hsl(120, 100%, 40%);">+               auto = ('PIN1', 'PIN2', 'PUK1', 'PUK2')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             if str(code).upper() not in auto:</span><br><span style="color: hsl(120, 100%, 40%);">+                     return sanitize_pin_adm(code)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               result = card_data_get_field(str(code), key='ICCID', value=self._cmd.iccid)</span><br><span style="color: hsl(120, 100%, 40%);">+           result = sanitize_pin_adm(result)</span><br><span style="color: hsl(120, 100%, 40%);">+             if result:</span><br><span style="color: hsl(120, 100%, 40%);">+                    self._cmd.poutput("found %s '%s' for ICCID '%s'" % (code.upper(), result, self._cmd.iccid))</span><br><span style="color: hsl(120, 100%, 40%);">+         else:</span><br><span style="color: hsl(120, 100%, 40%);">+                 self._cmd.poutput("cannot find %s for ICCID '%s'" % (code.upper(), self._cmd.iccid))</span><br><span style="color: hsl(120, 100%, 40%);">+                return result</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      verify_chv_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(0, 100%, 40%);">-   verify_chv_parser.add_argument('--chv-nr', type=int, default=1, help='CHV Number')</span><br><span style="color: hsl(0, 100%, 40%);">-      verify_chv_parser.add_argument('code', help='CODE/PIN/PUK')</span><br><span style="color: hsl(120, 100%, 40%);">+   verify_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PIN Number, 1=PIN1, 2=PIN2 or custom value (decimal)')</span><br><span style="color: hsl(120, 100%, 40%);">+  verify_chv_parser.add_argument('pin_code', type=str, help='PIN code digits, \"PIN1\" or \"PIN2\" to get PIN code from external data source')</span><br><span> </span><br><span>         @cmd2.with_argparser(verify_chv_parser)</span><br><span>      def do_verify_chv(self, opts):</span><br><span style="color: hsl(0, 100%, 40%);">-          """Verify (authenticate) using specified CHV (PIN)"""</span><br><span style="color: hsl(0, 100%, 40%);">-             (data, sw) = self._cmd.card._scc.verify_chv(opts.chv_nr, opts.code)</span><br><span style="color: hsl(0, 100%, 40%);">-             self._cmd.poutput(data)</span><br><span style="color: hsl(120, 100%, 40%);">+               """Verify (authenticate) using specified PIN code"""</span><br><span style="color: hsl(120, 100%, 40%);">+            pin = self.get_code(opts.pin_code)</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.card._scc.verify_chv(opts.pin_nr, h2b(pin))</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput("CHV verfication successful")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   unblock_chv_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        unblock_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PUK Number, 1=PIN1, 2=PIN2 or custom value (decimal)')</span><br><span style="color: hsl(120, 100%, 40%);">+ unblock_chv_parser.add_argument('puk_code', type=str, help='PUK code digits \"PUK1\" or \"PUK2\" to get PUK code from external data source')</span><br><span style="color: hsl(120, 100%, 40%);">+      unblock_chv_parser.add_argument('new_pin_code', type=str, help='PIN code digits \"PIN1\" or \"PIN2\" to get PIN code from external data source')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        @cmd2.with_argparser(unblock_chv_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+      def do_unblock_chv(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+               """Unblock PIN code using specified PUK code"""</span><br><span style="color: hsl(120, 100%, 40%);">+         new_pin = self.get_code(opts.new_pin_code)</span><br><span style="color: hsl(120, 100%, 40%);">+            puk = self.get_code(opts.puk_code)</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.card._scc.unblock_chv(opts.pin_nr, h2b(puk), h2b(new_pin))</span><br><span style="color: hsl(120, 100%, 40%);">+             self._cmd.poutput("CHV unblock successful")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       change_chv_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+ change_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PUK Number, 1=PIN1, 2=PIN2 or custom value (decimal)')</span><br><span style="color: hsl(120, 100%, 40%);">+  change_chv_parser.add_argument('pin_code', type=str, help='PIN code digits \"PIN1\" or \"PIN2\" to get PIN code from external data source')</span><br><span style="color: hsl(120, 100%, 40%);">+       change_chv_parser.add_argument('new_pin_code', type=str, help='PIN code digits \"PIN1\" or \"PIN2\" to get PIN code from external data source')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ @cmd2.with_argparser(change_chv_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+       def do_change_chv(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+                """Change PIN code to a new PIN code"""</span><br><span style="color: hsl(120, 100%, 40%);">+         new_pin = self.get_code(opts.new_pin_code)</span><br><span style="color: hsl(120, 100%, 40%);">+            pin = self.get_code(opts.pin_code)</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.card._scc.change_chv(opts.pin_nr, h2b(pin), h2b(new_pin))</span><br><span style="color: hsl(120, 100%, 40%);">+              self._cmd.poutput("CHV change successful")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        disable_chv_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        disable_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PIN Number, 1=PIN1, 2=PIN2 or custom value (decimal)')</span><br><span style="color: hsl(120, 100%, 40%);">+ disable_chv_parser.add_argument('pin_code', type=str, help='PIN code digits, \"PIN1\" or \"PIN2\" to get PIN code from external data source')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   @cmd2.with_argparser(disable_chv_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+      def do_disable_chv(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+               """Disable PIN code using specified PIN code"""</span><br><span style="color: hsl(120, 100%, 40%);">+         pin = self.get_code(opts.pin_code)</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.card._scc.disable_chv(opts.pin_nr, h2b(pin))</span><br><span style="color: hsl(120, 100%, 40%);">+           self._cmd.poutput("CHV disable successful")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       enable_chv_parser = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+ enable_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PIN Number, 1=PIN1, 2=PIN2 or custom value (decimal)')</span><br><span style="color: hsl(120, 100%, 40%);">+  enable_chv_parser.add_argument('pin_code', type=str, help='PIN code digits, \"PIN1\" or \"PIN2\" to get PIN code from external data source')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @cmd2.with_argparser(enable_chv_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+       def do_enable_chv(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+                """Enable PIN code using specified PIN code"""</span><br><span style="color: hsl(120, 100%, 40%);">+          pin = self.get_code(opts.pin_code)</span><br><span style="color: hsl(120, 100%, 40%);">+            (data, sw) = self._cmd.card._scc.enable_chv(opts.pin_nr, h2b(pin))</span><br><span style="color: hsl(120, 100%, 40%);">+            self._cmd.poutput("CHV enable successful")</span><br><span> </span><br><span>     dir_parser = argparse.ArgumentParser()</span><br><span>       dir_parser.add_argument('--fids', help='Show file identifiers', action='store_true')</span><br><span>diff --git a/pySim/card_data.py b/pySim/card_data.py</span><br><span>index 495c1f3..ebc63a6 100644</span><br><span>--- a/pySim/card_data.py</span><br><span>+++ b/pySim/card_data.py</span><br><span>@@ -26,7 +26,7 @@</span><br><span> </span><br><span> class CardData(object):</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        VALID_FIELD_NAMES = ['ICCID', 'ADM1', 'IMSI']</span><br><span style="color: hsl(120, 100%, 40%);">+ VALID_FIELD_NAMES = ['ICCID', 'ADM1', 'IMSI', 'PIN1', 'PIN2', 'PUK1', 'PUK2']</span><br><span> </span><br><span>    # check input parameters, but do nothing concrete yet</span><br><span>        def get_data(self, fields=[], key='ICCID', value=""):</span><br><span>diff --git a/pySim/commands.py b/pySim/commands.py</span><br><span>index 65c3891..5184a77 100644</span><br><span>--- a/pySim/commands.py</span><br><span>+++ b/pySim/commands.py</span><br><span>@@ -21,7 +21,8 @@</span><br><span> # along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span> #</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from pySim.utils import rpad, b2h</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.utils import rpad, b2h, sw_match</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.exceptions import SwMatchError</span><br><span> </span><br><span> class SimCardCommands(object):</span><br><span>        def __init__(self, transport):</span><br><span>@@ -219,9 +220,39 @@</span><br><span>        def reset_card(self):</span><br><span>                return self._tp.reset_card()</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        def verify_chv(self, chv_no, code):</span><br><span style="color: hsl(0, 100%, 40%);">-             fc = rpad(b2h(code), 16)</span><br><span style="color: hsl(120, 100%, 40%);">+      def _chv_process_sw(self, op_name, chv_no, pin_code, sw):</span><br><span style="color: hsl(120, 100%, 40%);">+             if sw_match(sw, '63cx'):</span><br><span style="color: hsl(120, 100%, 40%);">+                      raise RuntimeError('Failed to %s chv_no 0x%02X with code 0x%s, %i tries left.' %</span><br><span style="color: hsl(120, 100%, 40%);">+                                                         (op_name, chv_no, b2h(pin_code).upper(), int(sw[3])))</span><br><span style="color: hsl(120, 100%, 40%);">+              elif (sw != '9000'):</span><br><span style="color: hsl(120, 100%, 40%);">+                  raise SwMatchError(sw, '9000')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      def verify_chv(self, chv_no, pin_code):</span><br><span style="color: hsl(120, 100%, 40%);">+               fc = rpad(b2h(pin_code), 16)</span><br><span>                 data, sw = self._tp.send_apdu(self.cla_byte + '2000' + ('%02X' % chv_no) + '08' + fc)</span><br><span style="color: hsl(0, 100%, 40%);">-           if (sw != '9000'):</span><br><span style="color: hsl(0, 100%, 40%);">-                      raise RuntimeError('Failed to authenticate with ADM key %s, %i tries left.' % (code, int(sw[3])))</span><br><span style="color: hsl(0, 100%, 40%);">-               return (data,sw)</span><br><span style="color: hsl(120, 100%, 40%);">+              self._chv_process_sw('verify', chv_no, pin_code, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+          return (data, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   def unblock_chv(self, chv_no, puk_code, pin_code):</span><br><span style="color: hsl(120, 100%, 40%);">+            fc = rpad(b2h(puk_code), 16) + rpad(b2h(pin_code), 16)</span><br><span style="color: hsl(120, 100%, 40%);">+                data, sw = self._tp.send_apdu(self.cla_byte + '2C00' + ('%02X' % chv_no) + '10' + fc)</span><br><span style="color: hsl(120, 100%, 40%);">+         self._chv_process_sw('unblock', chv_no, pin_code, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+         return (data, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   def change_chv(self, chv_no, pin_code, new_pin_code):</span><br><span style="color: hsl(120, 100%, 40%);">+         fc = rpad(b2h(pin_code), 16) + rpad(b2h(new_pin_code), 16)</span><br><span style="color: hsl(120, 100%, 40%);">+            data, sw = self._tp.send_apdu(self.cla_byte + '2400' + ('%02X' % chv_no) + '10' + fc)</span><br><span style="color: hsl(120, 100%, 40%);">+         self._chv_process_sw('change', chv_no, pin_code, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+          return (data, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   def disable_chv(self, chv_no, pin_code):</span><br><span style="color: hsl(120, 100%, 40%);">+              fc = rpad(b2h(pin_code), 16)</span><br><span style="color: hsl(120, 100%, 40%);">+          data, sw = self._tp.send_apdu(self.cla_byte + '2600' + ('%02X' % chv_no) + '08' + fc)</span><br><span style="color: hsl(120, 100%, 40%);">+         self._chv_process_sw('disable', chv_no, pin_code, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+         return (data, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   def enable_chv(self, chv_no, pin_code):</span><br><span style="color: hsl(120, 100%, 40%);">+               fc = rpad(b2h(pin_code), 16)</span><br><span style="color: hsl(120, 100%, 40%);">+          data, sw = self._tp.send_apdu(self.cla_byte + '2800' + ('%02X' % chv_no) + '08' + fc)</span><br><span style="color: hsl(120, 100%, 40%);">+         self._chv_process_sw('enable', chv_no, pin_code, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+          return (data, sw)</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/pysim/+/23541">change 23541</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/+/23541"/><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: Ic89446e6bd2021095e579fb6b20458df48ba6413 </div>
<div style="display:none"> Gerrit-Change-Number: 23541 </div>
<div style="display:none"> Gerrit-PatchSet: 6 </div>
<div style="display:none"> Gerrit-Owner: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-CC: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>