diff --git a/pysim_simple_server/esim.py b/pysim_simple_server/esim.py index cf3056a..d18c65a 100644 --- a/pysim_simple_server/esim.py +++ b/pysim_simple_server/esim.py @@ -509,13 +509,15 @@ def parse_switch_response(action, data_hex): 'message': _error_text(result)} -def switch_profile(send_apdu, run_chain, action, iccid=None, isdp_aid=None, - refresh=True): - """Run an ES10c Enable/DisableProfile switch. +def switch_profile(app, send_apdu, run_chain, action, iccid=None, + isdp_aid=None, refresh=True): + """Run an ES10c Enable/DisableProfile switch on the ISD-R. ``send_apdu(apdu_hex) -> (data_hex, sw)`` performs one raw STORE DATA and ``run_chain(sw91)`` answers the proactive command(s) the card sends - alongside the switch (True when a REFRESH was answered). + alongside the switch (True when a REFRESH was answered). The ISD-R is + selected first and the previous selection restored afterwards, like the + other ES10 functions. With the refresh flag set the ISD-R returns OK *before* the REFRESH (SGP.22 v2.6 §5.7.16/§5.7.17 step 6) and the switch completes upon the @@ -523,18 +525,22 @@ def switch_profile(send_apdu, run_chain, action, iccid=None, isdp_aid=None, OK: the STORE DATA is never retried (the mid-switch card answers 6985 to the retry) and the caller re-initializes the card afterwards.""" apdu = build_switch_apdu(action, iccid, isdp_aid, refresh) - data, sw = send_apdu(apdu) - if sw == '9000': - out = parse_switch_response(action, data) - out['refresh_seen'] = False - return out - if sw and sw.startswith('91'): - refresh_seen = False - try: - refresh_seen = bool(run_chain(sw)) - except Exception as e: - sys.stderr.write('ESIM: REFRESH chain failed: %s\n' % e) - return {'ok': True, 'result': 'ok', 'message': 'ok', - 'refresh_seen': refresh_seen} - return {'ok': False, 'result': 'undefinedError', 'sw': sw, - 'message': 'SW %s' % sw} + _select_isdr(app) + try: + data, sw = send_apdu(apdu) + if sw == '9000': + out = parse_switch_response(action, data) + out['refresh_seen'] = False + return out + if sw and sw.startswith('91'): + refresh_seen = False + try: + refresh_seen = bool(run_chain(sw)) + except Exception as e: + sys.stderr.write('ESIM: REFRESH chain failed: %s\n' % e) + return {'ok': True, 'result': 'ok', 'message': 'ok', + 'refresh_seen': refresh_seen} + return {'ok': False, 'result': 'undefinedError', 'sw': sw, + 'message': 'SW %s' % sw} + finally: + _restore(app) diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py index 6c931ac..6a6510a 100644 --- a/pysim_simple_server/server.py +++ b/pysim_simple_server/server.py @@ -3861,7 +3861,7 @@ class PysimHandler(BaseHTTPRequestHandler): _finish_pending_menu(self.server, self.server.scc) cursor = _PROACTIVE_ENTRY_ID resp = esim.switch_profile( - self.server.scc._tp.send_apdu, + app, self.server.scc._tp.send_apdu, lambda sw: _esim_refresh_chain(self.server, sw), action, iccid=body.get('iccid'), isdp_aid=body.get('isdp_aid'), diff --git a/tests/test_esim.py b/tests/test_esim.py index ba258a6..cdfe4a9 100644 --- a/tests/test_esim.py +++ b/tests/test_esim.py @@ -239,9 +239,11 @@ class EsimTests(unittest.TestCase): self.assertEqual(out['result'], 'undefinedError') def test_switch_profile_parses_ok_response(self): + app, lchan = make_app() sent = [] resp = EnableProfileResp(children=[EnableResult(decoded='ok')]) out = esim.switch_profile( + app, lambda apdu: sent.append(apdu) or (resp.to_tlv().hex(), '9000'), lambda sw: self.fail('no chain expected'), 'enable', iccid='8970119000004002667') @@ -249,10 +251,15 @@ class EsimTests(unittest.TestCase): self.assertEqual(out['result'], 'ok') self.assertFalse(out['refresh_seen']) self.assertEqual(len(sent), 1) + # the ISD-R was selected for the command and the selection restored + self.assertEqual(lchan.selected.name, 'ISD-R') + self.assertEqual(app.rs.resets, 1) def test_switch_profile_treats_91xx_as_ok_and_runs_the_chain(self): + app, lchan = make_app() chain = [] out = esim.switch_profile( + app, lambda apdu: ('', '9111'), lambda sw: chain.append(sw) or True, 'disable', iccid='8970119000004002667') @@ -260,17 +267,22 @@ class EsimTests(unittest.TestCase): self.assertEqual(out['result'], 'ok') self.assertTrue(out['refresh_seen']) self.assertEqual(chain, ['9111']) + self.assertEqual(lchan.selected.name, 'ISD-R') + self.assertEqual(app.rs.resets, 1) def test_switch_profile_chain_failure_keeps_the_accepted_switch(self): + app, _ = make_app() + def boom(sw): raise RuntimeError('fetch failed') - out = esim.switch_profile(lambda apdu: ('', '910f'), boom, + out = esim.switch_profile(app, lambda apdu: ('', '910f'), boom, 'disable', iccid='8970119000004002667') self.assertTrue(out['ok']) self.assertFalse(out['refresh_seen']) def test_switch_profile_reports_error_sw(self): - out = esim.switch_profile(lambda apdu: ('', '6985'), + app, _ = make_app() + out = esim.switch_profile(app, lambda apdu: ('', '6985'), lambda sw: self.fail('no chain expected'), 'disable', iccid='8970119000004002667') self.assertFalse(out['ok'])