Hoernchen has uploaded this change for review.
smpp-ota-tool: add --format compact,expanded for TS 102 226 5.2
Add --format expanded flag to send C-APDUs in the expanded remote
application data format.
Each --apdu becomes its own C-APDU TLV, and the tool
logs the full per-command R-APDU list decoded from the Response Scripting
template and warns if the card reports a truncated response.
Default stays 'compact', unchanged.
Change-Id: Idad90756f85bb7e54ef720f6067bb5d6dcff0c42
---
M contrib/smpp-ota-tool.py
M docs/smpp-ota-tool.rst
2 files changed, 64 insertions(+), 7 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/42/43542/1
diff --git a/contrib/smpp-ota-tool.py b/contrib/smpp-ota-tool.py
index 28c13bf..3698001 100755
--- a/contrib/smpp-ota-tool.py
+++ b/contrib/smpp-ota-tool.py
@@ -70,6 +70,8 @@
option_parser.add_argument('--src-addr', default='12', type=str, help='SMS source address (MSISDN)')
option_parser.add_argument('--dest-addr', default='23', type=str, help='SMS destination address (MSISDN)')
option_parser.add_argument('--timeout', default=10, type=int, help='Maximum response waiting time')
+option_parser.add_argument('--format', choices=['compact', 'expanded'], default='compact',
+ help="Remote Application data format: 'compact' or 'expanded'")
option_parser.add_argument('-a', '--apdu', action='append', required=True, type=is_hexstr, help='C-APDU to send')
class SmppHandler:
@@ -77,7 +79,8 @@
def __init__(self, host: str, port: int,
system_id: str, password: str,
- ota_keyset: OtaKeyset, spi: dict, tar: bytes):
+ ota_keyset: OtaKeyset, spi: dict, tar: bytes,
+ remote_format: str = 'compact'):
"""
Initialize connection to SMPP server and set static OTA SMS-TPDU ciphering parameters
Args:
@@ -88,6 +91,7 @@
ota_keyset: OTA keyset to be used for SMS-TPDU ciphering
spi: Security Parameter Indicator (SPI) to be used for SMS-TPDU ciphering
tar: Toolkit Application Reference (TAR) of the targeted card application
+ remote_format: Remote Application data format ('compact' or 'expanded', TS 102 226)
"""
# Create and connect SMPP client
@@ -103,6 +107,7 @@
self.ota_keyset = ota_keyset
self.tar = tar
self.spi = spi
+ self.remote_format = remote_format
def __del__(self):
if self.client:
@@ -113,14 +118,16 @@
if pdu.short_message:
logger.info("SMS-TPDU received: %s", b2h(pdu.short_message))
try:
- dec = self.ota_dialect.decode_resp(self.ota_keyset, self.spi, pdu.short_message)
+ dec = self.ota_dialect.decode_resp(self.ota_keyset, self.spi, pdu.short_message,
+ remote_format=self.remote_format)
except ValueError:
# Retry to decoding with ciphering disabled (in case the card has problems to decode the SMS-TDPU
# we have sent, the response will contain an unencrypted error message)
spi = self.spi.copy()
spi['por_shall_be_ciphered'] = False
spi['por_rc_cc_ds'] = 'no_rc_cc_ds'
- dec = self.ota_dialect.decode_resp(self.ota_keyset, spi, pdu.short_message)
+ dec = self.ota_dialect.decode_resp(self.ota_keyset, spi, pdu.short_message,
+ remote_format=self.remote_format)
logger.info("SMS-TPDU decoded: %s", dec)
self.response = dec
return None
@@ -183,10 +190,14 @@
tuple containing the last response data and the last status word as byte strings
"""
- logger.info("C-APDU sending: %s...", b2h(apdu))
+ if isinstance(apdu, (list, tuple)):
+ logger.info("C-APDU(s) sending: %s...", [b2h(a) for a in apdu])
+ else:
+ logger.info("C-APDU sending: %s...", b2h(apdu))
# translate to Secured OTA RFM
- secured = self.ota_dialect.encode_cmd(self.ota_keyset, self.tar, self.spi, apdu=apdu)
+ secured = self.ota_dialect.encode_cmd(self.ota_keyset, self.tar, self.spi, apdu=apdu,
+ remote_format=self.remote_format)
# add user data header
tpdu = b'\x02\x70\x00' + secured
# send via SMPP
@@ -200,6 +211,17 @@
container_dict = dict(container)
resp = container_dict.get('last_response_data')
sw = container_dict.get('last_status_word')
+ # expanded format: decoded response carries
+ # per command R-APDU list; log each one.
+ for i, cmd in enumerate(container_dict.get('commands') or []):
+ logger.info("R-APDU[%u] received: %s %s", i,
+ cmd['response_data'], cmd['status_word'])
+ if container_dict.get('truncated'):
+ logger.warning("Response was TRUNCATED (SW 62F1): the card cut the response "
+ "data short and did not execute the rest of the script")
+ if container_dict.get('bad_format') is not None:
+ logger.warning("Response contains a Bad format TLV: %s",
+ container_dict['bad_format'])
if resp is None:
raise ValueError("Response does not contain any last_response_data, no R-APDU received!")
if sw is None:
@@ -233,8 +255,14 @@
'por_shall_be_ciphered': not opts.por_no_ciphering,
'por_rc_cc_ds': opts.por_rc_cc_ds,
'por': opts.por_req}
- apdu = h2b("".join(opts.apdu))
+ if opts.format == 'expanded':
+ # TS 102 226 5.2.1.1: wrap each apdu in its own C-APDU TLV
+ apdu = [h2b(a) for a in opts.apdu]
+ else:
+ # compact: C-APDUs are concatenated as single command string
+ apdu = h2b("".join(opts.apdu))
- smpp_handler = SmppHandler(opts.host, opts.port, opts.system_id, opts.password, ota_keyset, spi, h2b(opts.tar))
+ smpp_handler = SmppHandler(opts.host, opts.port, opts.system_id, opts.password, ota_keyset, spi,
+ h2b(opts.tar), remote_format=opts.format)
resp, sw = smpp_handler.transceive_apdu(apdu, opts.src_addr, opts.dest_addr, opts.timeout)
print("%s %s" % (b2h(resp), b2h(sw)))
diff --git a/docs/smpp-ota-tool.rst b/docs/smpp-ota-tool.rst
index beb494a..9dc0df6 100644
--- a/docs/smpp-ota-tool.rst
+++ b/docs/smpp-ota-tool.rst
@@ -170,6 +170,35 @@
.. note:: The replay-protection-counter is implemented as a 5 byte integer value (see also ETSI TS 102 225, Table 3).
When the counter has reached its maximum, it will not overflow nor can it be reset.
+Expanded remote application data format
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`smpp-ota-tool` uses the TS 102 226 section 5.1 compact remote application data format by default. This
+format concatenates C-APDUs into one command string and only the result of the LAST executed command is reported back.
+Retrieving the response data therefore requires a GET RESPONSE C-APDU, and only a single GET RESPONSE command may occur per script.
+
+The TS 102 226 section 5.2 expanded remote application data format removes these limitations: Each C-APDU is
+wrapped in its own C-APDU TLV inside a Command Scripting template, and the response is a Response Scripting template that contains one R-APDU TLV with the full response data and status word per executed command. To use it, pass
+``--format expanded``; every ``--apdu`` argument then becomes its own C-APDU TLV.
+
+.. note:: The expanded format does not use GET RESPONSE. To retrieve response data from a case 2 or case 4
+ command, include an ``Le`` field in the C-APDU. i.e. ``Le='00'`` instructs the card to return all available
+ response data in the R-APDU, with no 256-byte limit (TS 102 226, section 5.2.1.1). Without the ``Le``
+ field no response data is returned, except a status word for the last command!.
+
+For example, a GP GET STATUS of all applications (``80F24002024F00``) returns a registry that can be much
+larger than 256 bytes. In the compact format the card would only answer with ``61xx`` procedure bytes. In the expanded
+format, appending ``Le='00'`` (i.e. ``80F24002024F0000``) makes the card return the whole registry in one exchange:
+
+::
+
+ $ PYTHONPATH=./ ./contrib/smpp-ota-tool.py --kic <KIC> --kid <KID> --kid-idx 1 --kic-idx 1 \
+ --algo-crypt triple_des_cbc2 --algo-auth triple_des_cbc2 --tar 000000 --cntr-req no_counter \
+ --format expanded --apdu 80F24002024F0000
+
+The response data (a concatenation of GlobalPlatform registry TLVs) can then be decoded with
+``pySim.global_platform.GpRegistryRelatedData.from_tlv()``.
+
smpp-ota-tool syntax
~~~~~~~~~~~~~~~~~~~~
To view, visit change 43542. To unsubscribe, or for help writing mail filters, visit settings.