laforge submitted this change.

View Change

Approvals: laforge: Looks good to me, approved fixeria: Looks good to me, but someone else must approve Jenkins Builder: Verified
pySim/transport: fix GET RESPONSE behaviour

The current behavior we implement in the method __send_apdu_T0 is
incomplete. Some details discussed in ETSI TS 102 221,
section 7.3.1.1.4, clause 4 seem to be not fully implemented. We
may also end up sending a GET RESPONSE in other APDU cases than
case 4 (the only case that uses the GET RESPONSE command).

Related: OS#6970
Change-Id: I26f0566af0cdd61dcc97f5f502479dc76adc37cc
---
M pySim/transport/__init__.py
M tests/pySim-shell_test/apdu/test_apdu.script
2 files changed, 60 insertions(+), 16 deletions(-)

diff --git a/pySim/transport/__init__.py b/pySim/transport/__init__.py
index 6833a90..d689a0a 100644
--- a/pySim/transport/__init__.py
+++ b/pySim/transport/__init__.py
@@ -301,24 +301,54 @@

prev_tpdu = tpdu
data, sw = self.send_tpdu(tpdu)
+ log.debug("T0: case #%u TPDU: %s => %s %s", case, tpdu, data or "(no data)", sw or "(no status word)")
+ if sw is None:
+ raise ValueError("no status word received")

- # When we have sent the first APDU, the SW may indicate that there are response bytes
- # available. There are two SWs commonly used for this 9fxx (sim) and 61xx (usim), where
- # xx is the number of response bytes available.
- # See also:
- if sw is not None:
- while (sw[0:2] in ['9f', '61', '62', '63']):
- # SW1=9F: 3GPP TS 51.011 9.4.1, Responses to commands which are correctly executed
- # SW1=61: ISO/IEC 7816-4, Table 5 — General meaning of the interindustry values of SW1-SW2
- # SW1=62: ETSI TS 102 221 7.3.1.1.4 Clause 4b): 62xx, 63xx, 9xxx != 9000
- tpdu_gr = tpdu[0:2] + 'c00000' + sw[2:4]
+ # After sending the APDU/TPDU the UICC/eUICC or SIM may response with a status word that indicates that further
+ # TPDUs have to be sent in order to complete the task.
+ if case == 4 or self.apdu_strict == False:
+ # In case the APDU is a case #4 APDU, the UICC/eUICC/SIM may indicate that there is response data
+ # available which has to be retrieved using a GET RESPONSE command TPDU.
+ #
+ # ETSI TS 102 221, section 7.3.1.1.4 is very cleare about the fact that the GET RESPONSE mechanism
+ # shall only apply on case #4 APDUs but unfortunately it is impossible to distinguish between case #3
+ # and case #4 when the APDU format is not strictly followed. In order to be able to detect case #4
+ # correctly the Le byte (usually 0x00) must be present, is often forgotten. To avoid problems with
+ # legacy scripts that use raw APDU strings, we will still loosely apply GET RESPONSE based on what
+ # the status word indicates. Unless the user explicitly enables the strict mode (set apdu_strict true)
+ while True:
+ if sw in ['9000', '9100']:
+ # A status word of 9000 (or 9100 in case there is pending data from a proactive SIM command)
+ # indicates that either no response data was returnd or all response data has been retrieved
+ # successfully. We may discontinue the processing at this point.
+ break;
+ if sw[0:2] in ['61', '9f']:
+ # A status word of 61xx or 9fxx indicates that there is (still) response data available. We
+ # send a GET RESPONSE command with the length value indicated in the second byte of the status
+ # word. (see also ETSI TS 102 221, section 7.3.1.1.4, clause 4a and 3GPP TS 51.011 9.4.1 and
+ # ISO/IEC 7816-4, Table 5)
+ le_gr = sw[2:4]
+ elif sw[0:2] in ['62', '63']:
+ # There are corner cases (status word is 62xx or 63xx) where the UICC/eUICC/SIM asks us
+ # to send a dummy GET RESPONSE command. We send a GET RESPONSE command with a length of 0.
+ # (see also ETSI TS 102 221, section 7.3.1.1.4, clause 4b and ETSI TS 151 011, section 9.4.1)
+ le_gr = '00'
+ else:
+ # A status word other then the ones covered by the above logic may indicate an error. In this
+ # case we will discontinue the processing as well.
+ # (see also ETSI TS 102 221, section 7.3.1.1.4, clause 4c)
+ break
+ tpdu_gr = tpdu[0:2] + 'c00000' + le_gr
prev_tpdu = tpdu_gr
- d, sw = self.send_tpdu(tpdu_gr)
- data += d
- if sw[0:2] == '6c':
- # SW1=6C: ETSI TS 102 221 Table 7.1: Procedure byte coding
- tpdu_gr = prev_tpdu[0:8] + sw[2:4]
- data, sw = self.send_tpdu(tpdu_gr)
+ data_gr, sw = self.send_tpdu(tpdu_gr)
+ log.debug("T0: GET RESPONSE TPDU: %s => %s %s", tpdu_gr, data_gr or "(no data)", sw or "(no status word)")
+ data += data_gr
+ if sw[0:2] == '6c':
+ # SW1=6C: ETSI TS 102 221 Table 7.1: Procedure byte coding
+ tpdu_gr = prev_tpdu[0:8] + sw[2:4]
+ data, sw = self.send_tpdu(tpdu_gr)
+ log.debug("T0: repated case #%u TPDU: %s => %s %s", case, tpdu_gr, data or "(no data)", sw or "(no status word)")

return data, sw

diff --git a/tests/pySim-shell_test/apdu/test_apdu.script b/tests/pySim-shell_test/apdu/test_apdu.script
index a949b2d..2531987 100644
--- a/tests/pySim-shell_test/apdu/test_apdu.script
+++ b/tests/pySim-shell_test/apdu/test_apdu.script
@@ -7,10 +7,24 @@
# No command data field, No response data field present
apdu 00700001 --expect-sw 9000 --expect-response-regex '^$'

+# Case #1: (verify pin)
+# This command returns the number of remaining authentication attempts in the
+# form of a status that has the form 63cX, where X is the number of remaining
+# attempts. Such a status word can be easily confused with the response to a
+# case #4 APDU. This test checks if the transport layer correctly distinguishes
+# the between APDU case #1 and APDU case #4.
+apdu 0020000A --expect-sw 63c? --expect-response-regex '^$'
+
# Case #2: (status)
# No command data field, Response data field present
apdu 80F2000000 --expect-sw 9000 --expect-response-regex '^[a-fA-F0-9]+$'

+# Case #2: (verify pin)
+# (see also above). This test checks if the transport layer is also able to
+# distinguish correctly between APDU case #2 (with zero length response) and
+# APDU case #4.
+apdu 0020000A00 --expect-sw 63c? --expect-response-regex '^$'
+
# Case #3: (terminal capability)
# Command data field present, No response data field
apdu 80AA000005a903830180 --expect-sw 9000 --expect-response-regex '^$'

To view, visit change 42301. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: merged
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I26f0566af0cdd61dcc97f5f502479dc76adc37cc
Gerrit-Change-Number: 42301
Gerrit-PatchSet: 9
Gerrit-Owner: dexter <pmaier@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: laforge <laforge@osmocom.org>