proactive: Send a Terminal Response automatically after a Fetch

Change-Id: I43bc994e7517b5907fb40a98d84797c54056c47d
This commit is contained in:
Christian Amsüss 2022-08-12 15:46:52 +02:00 committed by laforge
parent 98552ef1bd
commit 59f3b1154f
1 changed files with 43 additions and 7 deletions

View File

@ -10,7 +10,7 @@ from typing import Optional, Tuple
from pySim.exceptions import * from pySim.exceptions import *
from pySim.construct import filter_dict from pySim.construct import filter_dict
from pySim.utils import sw_match, b2h, h2b, i2h, Hexstr from pySim.utils import sw_match, b2h, h2b, i2h, Hexstr
from pySim.cat import ProactiveCommand from pySim.cat import ProactiveCommand, CommandDetails, DeviceIdentities, Result
# #
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com> # Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
@ -42,10 +42,7 @@ class ProactiveHandler(abc.ABC):
"""Abstract base class representing the interface of some code that handles """Abstract base class representing the interface of some code that handles
the proactive commands, as returned by the card in responses to the FETCH the proactive commands, as returned by the card in responses to the FETCH
command.""" command."""
def receive_fetch_raw(self, payload: Hexstr): def receive_fetch_raw(self, pcmd: ProactiveCommand, parsed: Hexstr):
# parse the proactive command
pcmd = ProactiveCommand()
parsed = pcmd.from_tlv(h2b(payload))
# try to find a generic handler like handle_SendShortMessage # try to find a generic handler like handle_SendShortMessage
handle_name = 'handle_%s' % type(parsed).__name__ handle_name = 'handle_%s' % type(parsed).__name__
if hasattr(self, handle_name): if hasattr(self, handle_name):
@ -167,11 +164,50 @@ class LinkBase(abc.ABC):
# need not concern the caller. # need not concern the caller.
rv = (rv[0], '9000') rv = (rv[0], '9000')
# proactive sim as per TS 102 221 Setion 7.4.2 # proactive sim as per TS 102 221 Setion 7.4.2
# TODO: Check SW manually to avoid recursing on the stack (provided this piece of code stays in this place)
fetch_rv = self.send_apdu_checksw('80120000' + last_sw[2:], sw) fetch_rv = self.send_apdu_checksw('80120000' + last_sw[2:], sw)
# Setting this in case we later decide not to send a terminal
# response immediately unconditionally -- the card may still have
# something pending even though the last command was not processed
# yet.
last_sw = fetch_rv[1] last_sw = fetch_rv[1]
print("FETCH: %s" % fetch_rv[0]) # parse the proactive command
pcmd = ProactiveCommand()
parsed = pcmd.from_tlv(h2b(fetch_rv[0]))
print("FETCH: %s (%s)" % (fetch_rv[0], type(parsed).__name__))
result = Result()
if self.proactive_handler: if self.proactive_handler:
self.proactive_handler.receive_fetch_raw(fetch_rv[0]) # Extension point: If this does return a list of TLV objects,
# they could be appended after the Result; if the first is a
# Result, that cuold replace the one built here.
self.proactive_handler.receive_fetch_raw(pcmd, parsed)
result.from_dict({'general_result': 'performed_successfully', 'additional_information': ''})
else:
result.from_dict({'general_result': 'command_beyond_terminal_capability', 'additional_information': ''})
# Send response immediately, thus also flushing out any further
# proactive commands that the card already wants to send
#
# Structure as per TS 102 223 V4.4.0 Section 6.8
# The Command Details are echoed from the command that has been processed.
(command_details,) = [c for c in pcmd.decoded.children if isinstance(c, CommandDetails)]
# The Device Identities are fixed. (TS 102 223 V4.0.0 Section 6.8.2)
device_identities = DeviceIdentities()
device_identities.from_dict({'source_dev_id': 'terminal', 'dest_dev_id': 'uicc'})
# Testing hint: The value of tail does not influence the behavior
# of an SJA2 that sent ans SMS, so this is implemented only
# following TS 102 223, and not fully tested.
tail = command_details.to_tlv() + device_identities.to_tlv() + result.to_tlv()
# Testing hint: In contrast to the above, this part is positively
# essential to get the SJA2 to provide the later parts of a
# multipart SMS in response to an OTA RFM command.
terminal_response = '80140000' + b2h(len(tail).to_bytes(1, 'big') + tail)
terminal_response_rv = self.send_apdu(terminal_response)
last_sw = terminal_response_rv[1]
if not sw_match(rv[1], sw): if not sw_match(rv[1], sw):
raise SwMatchError(rv[1], sw.lower(), self.sw_interpreter) raise SwMatchError(rv[1], sw.lower(), self.sw_interpreter)
return rv return rv