cosmetic: Switch to consistent four-spaces indent; run autopep8
We had a mixture of tab and 4space based indenting, which is a bad
idea. 4space is the standard in python, so convert all our code to
that. The result unfortuantely still shoed even more inconsistencies,
so I've decided to run autopep8 on the entire code base.
Change-Id: I4a4b1b444a2f43fab05fc5d2c8a7dd6ddecb5f07
diff --git a/pySim/transport/__init__.py b/pySim/transport/__init__.py
index cb781b0..9364b07 100644
--- a/pySim/transport/__init__.py
+++ b/pySim/transport/__init__.py
@@ -29,217 +29,224 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-class ApduTracer:
- def trace_command(self, cmd):
- pass
- def trace_response(self, cmd, sw, resp):
- pass
+class ApduTracer:
+ def trace_command(self, cmd):
+ pass
+
+ def trace_response(self, cmd, sw, resp):
+ pass
class LinkBase(abc.ABC):
- """Base class for link/transport to card."""
+ """Base class for link/transport to card."""
- def __init__(self, sw_interpreter=None, apdu_tracer=None):
- self.sw_interpreter = sw_interpreter
- self.apdu_tracer = apdu_tracer
+ def __init__(self, sw_interpreter=None, apdu_tracer=None):
+ self.sw_interpreter = sw_interpreter
+ self.apdu_tracer = apdu_tracer
- @abc.abstractmethod
- def _send_apdu_raw(self, pdu:str) -> Tuple[str, str]:
- """Implementation specific method for sending the PDU."""
+ @abc.abstractmethod
+ def _send_apdu_raw(self, pdu: str) -> Tuple[str, str]:
+ """Implementation specific method for sending the PDU."""
- def set_sw_interpreter(self, interp):
- """Set an (optional) status word interpreter."""
- self.sw_interpreter = interp
+ def set_sw_interpreter(self, interp):
+ """Set an (optional) status word interpreter."""
+ self.sw_interpreter = interp
- @abc.abstractmethod
- def wait_for_card(self, timeout:int=None, newcardonly:bool=False):
- """Wait for a card and connect to it
+ @abc.abstractmethod
+ def wait_for_card(self, timeout: int = None, newcardonly: bool = False):
+ """Wait for a card and connect to it
- Args:
- timeout : Maximum wait time in seconds (None=no timeout)
- newcardonly : Should we wait for a new card, or an already inserted one ?
- """
+ Args:
+ timeout : Maximum wait time in seconds (None=no timeout)
+ newcardonly : Should we wait for a new card, or an already inserted one ?
+ """
- @abc.abstractmethod
- def connect(self):
- """Connect to a card immediately
- """
+ @abc.abstractmethod
+ def connect(self):
+ """Connect to a card immediately
+ """
- @abc.abstractmethod
- def disconnect(self):
- """Disconnect from card
- """
+ @abc.abstractmethod
+ def disconnect(self):
+ """Disconnect from card
+ """
- @abc.abstractmethod
- def reset_card(self):
- """Resets the card (power down/up)
- """
+ @abc.abstractmethod
+ def reset_card(self):
+ """Resets the card (power down/up)
+ """
- def send_apdu_raw(self, pdu:str):
- """Sends an APDU with minimal processing
+ def send_apdu_raw(self, pdu: str):
+ """Sends an APDU with minimal processing
- Args:
- pdu : string of hexadecimal characters (ex. "A0A40000023F00")
- Returns:
- tuple(data, sw), where
- data : string (in hex) of returned data (ex. "074F4EFFFF")
- sw : string (in hex) of status word (ex. "9000")
- """
- if self.apdu_tracer:
- self.apdu_tracer.trace_command(pdu)
- (data, sw) = self._send_apdu_raw(pdu)
- if self.apdu_tracer:
- self.apdu_tracer.trace_response(pdu, sw, data)
- return (data, sw)
+ Args:
+ pdu : string of hexadecimal characters (ex. "A0A40000023F00")
+ Returns:
+ tuple(data, sw), where
+ data : string (in hex) of returned data (ex. "074F4EFFFF")
+ sw : string (in hex) of status word (ex. "9000")
+ """
+ if self.apdu_tracer:
+ self.apdu_tracer.trace_command(pdu)
+ (data, sw) = self._send_apdu_raw(pdu)
+ if self.apdu_tracer:
+ self.apdu_tracer.trace_response(pdu, sw, data)
+ return (data, sw)
- def send_apdu(self, pdu):
- """Sends an APDU and auto fetch response data
+ def send_apdu(self, pdu):
+ """Sends an APDU and auto fetch response data
- Args:
- pdu : string of hexadecimal characters (ex. "A0A40000023F00")
- Returns:
- tuple(data, sw), where
- data : string (in hex) of returned data (ex. "074F4EFFFF")
- sw : string (in hex) of status word (ex. "9000")
- """
- data, sw = self.send_apdu_raw(pdu)
+ Args:
+ pdu : string of hexadecimal characters (ex. "A0A40000023F00")
+ Returns:
+ tuple(data, sw), where
+ data : string (in hex) of returned data (ex. "074F4EFFFF")
+ sw : string (in hex) of status word (ex. "9000")
+ """
+ data, sw = self.send_apdu_raw(pdu)
- # When whe 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):
- if ((sw[0:2] == '9f') or (sw[0:2] == '61')):
- # 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
- pdu_gr = pdu[0:2] + 'c00000' + sw[2:4]
- data, sw = self.send_apdu_raw(pdu_gr)
- if sw[0:2] == '6c':
- # SW1=6C: ETSI TS 102 221 Table 7.1: Procedure byte coding
- pdu_gr = pdu[0:8] + sw[2:4]
- data,sw = self.send_apdu_raw(pdu_gr)
+ # 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):
+ if ((sw[0:2] == '9f') or (sw[0:2] == '61')):
+ # 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
+ pdu_gr = pdu[0:2] + 'c00000' + sw[2:4]
+ data, sw = self.send_apdu_raw(pdu_gr)
+ if sw[0:2] == '6c':
+ # SW1=6C: ETSI TS 102 221 Table 7.1: Procedure byte coding
+ pdu_gr = pdu[0:8] + sw[2:4]
+ data, sw = self.send_apdu_raw(pdu_gr)
- return data, sw
+ return data, sw
- def send_apdu_checksw(self, pdu, sw="9000"):
- """Sends an APDU and check returned SW
+ def send_apdu_checksw(self, pdu, sw="9000"):
+ """Sends an APDU and check returned SW
- Args:
- pdu : string of hexadecimal characters (ex. "A0A40000023F00")
- sw : string of 4 hexadecimal characters (ex. "9000"). The user may mask out certain
- digits using a '?' to add some ambiguity if needed.
- Returns:
- tuple(data, sw), where
- data : string (in hex) of returned data (ex. "074F4EFFFF")
- sw : string (in hex) of status word (ex. "9000")
- """
- rv = self.send_apdu(pdu)
+ Args:
+ pdu : string of hexadecimal characters (ex. "A0A40000023F00")
+ sw : string of 4 hexadecimal characters (ex. "9000"). The user may mask out certain
+ digits using a '?' to add some ambiguity if needed.
+ Returns:
+ tuple(data, sw), where
+ data : string (in hex) of returned data (ex. "074F4EFFFF")
+ sw : string (in hex) of status word (ex. "9000")
+ """
+ rv = self.send_apdu(pdu)
- if sw == '9000' and sw_match(rv[1], '91xx'):
- # proactive sim as per TS 102 221 Setion 7.4.2
- rv = self.send_apdu_checksw('80120000' + rv[1][2:], sw)
- print("FETCH: %s", rv[0])
- if not sw_match(rv[1], sw):
- raise SwMatchError(rv[1], sw.lower(), self.sw_interpreter)
- return rv
+ if sw == '9000' and sw_match(rv[1], '91xx'):
+ # proactive sim as per TS 102 221 Setion 7.4.2
+ rv = self.send_apdu_checksw('80120000' + rv[1][2:], sw)
+ print("FETCH: %s", rv[0])
+ if not sw_match(rv[1], sw):
+ raise SwMatchError(rv[1], sw.lower(), self.sw_interpreter)
+ return rv
- def send_apdu_constr(self, cla, ins, p1, p2, cmd_constr, cmd_data, resp_constr):
- """Build and sends an APDU using a 'construct' definition; parses response.
+ def send_apdu_constr(self, cla, ins, p1, p2, cmd_constr, cmd_data, resp_constr):
+ """Build and sends an APDU using a 'construct' definition; parses response.
- Args:
- cla : string (in hex) ISO 7816 class byte
- ins : string (in hex) ISO 7816 instruction byte
- p1 : string (in hex) ISO 7116 Parameter 1 byte
- p2 : string (in hex) ISO 7116 Parameter 2 byte
- cmd_cosntr : defining how to generate binary APDU command data
- cmd_data : command data passed to cmd_constr
- resp_cosntr : defining how to decode binary APDU response data
- Returns:
- Tuple of (decoded_data, sw)
- """
- cmd = cmd_constr.build(cmd_data) if cmd_data else ''
- p3 = i2h([len(cmd)])
- pdu = ''.join([cla, ins, p1, p2, p3, b2h(cmd)])
- (data, sw) = self.send_apdu(pdu)
- if data:
- # filter the resulting dict to avoid '_io' members inside
- rsp = filter_dict(resp_constr.parse(h2b(data)))
- else:
- rsp = None
- return (rsp, sw)
+ Args:
+ cla : string (in hex) ISO 7816 class byte
+ ins : string (in hex) ISO 7816 instruction byte
+ p1 : string (in hex) ISO 7116 Parameter 1 byte
+ p2 : string (in hex) ISO 7116 Parameter 2 byte
+ cmd_cosntr : defining how to generate binary APDU command data
+ cmd_data : command data passed to cmd_constr
+ resp_cosntr : defining how to decode binary APDU response data
+ Returns:
+ Tuple of (decoded_data, sw)
+ """
+ cmd = cmd_constr.build(cmd_data) if cmd_data else ''
+ p3 = i2h([len(cmd)])
+ pdu = ''.join([cla, ins, p1, p2, p3, b2h(cmd)])
+ (data, sw) = self.send_apdu(pdu)
+ if data:
+ # filter the resulting dict to avoid '_io' members inside
+ rsp = filter_dict(resp_constr.parse(h2b(data)))
+ else:
+ rsp = None
+ return (rsp, sw)
- def send_apdu_constr_checksw(self, cla, ins, p1, p2, cmd_constr, cmd_data, resp_constr,
- sw_exp="9000"):
- """Build and sends an APDU using a 'construct' definition; parses response.
+ def send_apdu_constr_checksw(self, cla, ins, p1, p2, cmd_constr, cmd_data, resp_constr,
+ sw_exp="9000"):
+ """Build and sends an APDU using a 'construct' definition; parses response.
- Args:
- cla : string (in hex) ISO 7816 class byte
- ins : string (in hex) ISO 7816 instruction byte
- p1 : string (in hex) ISO 7116 Parameter 1 byte
- p2 : string (in hex) ISO 7116 Parameter 2 byte
- cmd_cosntr : defining how to generate binary APDU command data
- cmd_data : command data passed to cmd_constr
- resp_cosntr : defining how to decode binary APDU response data
- exp_sw : string (in hex) of status word (ex. "9000")
- Returns:
- Tuple of (decoded_data, sw)
- """
- (rsp, sw) = self.send_apdu_constr(cla, ins, p1, p2, cmd_constr, cmd_data, resp_constr)
- if not sw_match(sw, sw_exp):
- raise SwMatchError(sw, sw_exp.lower(), self.sw_interpreter)
- return (rsp, sw)
+ Args:
+ cla : string (in hex) ISO 7816 class byte
+ ins : string (in hex) ISO 7816 instruction byte
+ p1 : string (in hex) ISO 7116 Parameter 1 byte
+ p2 : string (in hex) ISO 7116 Parameter 2 byte
+ cmd_cosntr : defining how to generate binary APDU command data
+ cmd_data : command data passed to cmd_constr
+ resp_cosntr : defining how to decode binary APDU response data
+ exp_sw : string (in hex) of status word (ex. "9000")
+ Returns:
+ Tuple of (decoded_data, sw)
+ """
+ (rsp, sw) = self.send_apdu_constr(cla, ins,
+ p1, p2, cmd_constr, cmd_data, resp_constr)
+ if not sw_match(sw, sw_exp):
+ raise SwMatchError(sw, sw_exp.lower(), self.sw_interpreter)
+ return (rsp, sw)
+
def argparse_add_reader_args(arg_parser):
- """Add all reader related arguments to the given argparse.Argumentparser instance."""
- serial_group = arg_parser.add_argument_group('Serial Reader')
- serial_group.add_argument('-d', '--device', metavar='DEV', default='/dev/ttyUSB0',
- help='Serial Device for SIM access')
- serial_group.add_argument('-b', '--baud', dest='baudrate', type=int, metavar='BAUD', default=9600,
- help='Baud rate used for SIM access')
+ """Add all reader related arguments to the given argparse.Argumentparser instance."""
+ serial_group = arg_parser.add_argument_group('Serial Reader')
+ serial_group.add_argument('-d', '--device', metavar='DEV', default='/dev/ttyUSB0',
+ help='Serial Device for SIM access')
+ serial_group.add_argument('-b', '--baud', dest='baudrate', type=int, metavar='BAUD', default=9600,
+ help='Baud rate used for SIM access')
- pcsc_group = arg_parser.add_argument_group('PC/SC Reader')
- pcsc_group.add_argument('-p', '--pcsc-device', type=int, dest='pcsc_dev', metavar='PCSC', default=None,
- help='PC/SC reader number to use for SIM access')
+ pcsc_group = arg_parser.add_argument_group('PC/SC Reader')
+ pcsc_group.add_argument('-p', '--pcsc-device', type=int, dest='pcsc_dev', metavar='PCSC', default=None,
+ help='PC/SC reader number to use for SIM access')
- modem_group = arg_parser.add_argument_group('AT Command Modem Reader')
- modem_group.add_argument('--modem-device', dest='modem_dev', metavar='DEV', default=None,
- help='Serial port of modem for Generic SIM Access (3GPP TS 27.007)')
- modem_group.add_argument('--modem-baud', type=int, metavar='BAUD', default=115200,
- help='Baud rate used for modem port')
+ modem_group = arg_parser.add_argument_group('AT Command Modem Reader')
+ modem_group.add_argument('--modem-device', dest='modem_dev', metavar='DEV', default=None,
+ help='Serial port of modem for Generic SIM Access (3GPP TS 27.007)')
+ modem_group.add_argument('--modem-baud', type=int, metavar='BAUD', default=115200,
+ help='Baud rate used for modem port')
- osmobb_group = arg_parser.add_argument_group('OsmocomBB Reader')
- osmobb_group.add_argument('--osmocon', dest='osmocon_sock', metavar='PATH', default=None,
- help='Socket path for Calypso (e.g. Motorola C1XX) based reader (via OsmocomBB)')
+ osmobb_group = arg_parser.add_argument_group('OsmocomBB Reader')
+ osmobb_group.add_argument('--osmocon', dest='osmocon_sock', metavar='PATH', default=None,
+ help='Socket path for Calypso (e.g. Motorola C1XX) based reader (via OsmocomBB)')
- return arg_parser
+ return arg_parser
+
def init_reader(opts, **kwargs) -> Optional[LinkBase]:
- """
- Init card reader driver
- """
- sl = None # type : :Optional[LinkBase]
- try:
- if opts.pcsc_dev is not None:
- print("Using PC/SC reader interface")
- from pySim.transport.pcsc import PcscSimLink
- sl = PcscSimLink(opts.pcsc_dev, **kwargs)
- elif opts.osmocon_sock is not None:
- print("Using Calypso-based (OsmocomBB) reader interface")
- from pySim.transport.calypso import CalypsoSimLink
- sl = CalypsoSimLink(sock_path=opts.osmocon_sock, **kwargs)
- elif opts.modem_dev is not None:
- print("Using modem for Generic SIM Access (3GPP TS 27.007)")
- from pySim.transport.modem_atcmd import ModemATCommandLink
- sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud, **kwargs)
- else: # Serial reader is default
- print("Using serial reader interface")
- from pySim.transport.serial import SerialSimLink
- sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate, **kwargs)
- return sl
- except Exception as e:
- if str(e):
- print("Card reader initialization failed with exception:\n" + str(e))
- else:
- print("Card reader initialization failed with an exception of type:\n" + str(type(e)))
- return None
+ """
+ Init card reader driver
+ """
+ sl = None # type : :Optional[LinkBase]
+ try:
+ if opts.pcsc_dev is not None:
+ print("Using PC/SC reader interface")
+ from pySim.transport.pcsc import PcscSimLink
+ sl = PcscSimLink(opts.pcsc_dev, **kwargs)
+ elif opts.osmocon_sock is not None:
+ print("Using Calypso-based (OsmocomBB) reader interface")
+ from pySim.transport.calypso import CalypsoSimLink
+ sl = CalypsoSimLink(sock_path=opts.osmocon_sock, **kwargs)
+ elif opts.modem_dev is not None:
+ print("Using modem for Generic SIM Access (3GPP TS 27.007)")
+ from pySim.transport.modem_atcmd import ModemATCommandLink
+ sl = ModemATCommandLink(
+ device=opts.modem_dev, baudrate=opts.modem_baud, **kwargs)
+ else: # Serial reader is default
+ print("Using serial reader interface")
+ from pySim.transport.serial import SerialSimLink
+ sl = SerialSimLink(device=opts.device,
+ baudrate=opts.baudrate, **kwargs)
+ return sl
+ except Exception as e:
+ if str(e):
+ print("Card reader initialization failed with exception:\n" + str(e))
+ else:
+ print(
+ "Card reader initialization failed with an exception of type:\n" + str(type(e)))
+ return None
diff --git a/pySim/transport/calypso.py b/pySim/transport/calypso.py
index b55a089..15c9e5f 100644
--- a/pySim/transport/calypso.py
+++ b/pySim/transport/calypso.py
@@ -25,127 +25,132 @@
from pySim.exceptions import *
from pySim.utils import h2b, b2h
+
class L1CTLMessage(object):
- # Every (encoded) L1CTL message has the following structure:
- # - msg_length (2 bytes, net order)
- # - l1ctl_hdr (packed structure)
- # - msg_type
- # - flags
- # - padding (2 spare bytes)
- # - ... payload ...
+ # Every (encoded) L1CTL message has the following structure:
+ # - msg_length (2 bytes, net order)
+ # - l1ctl_hdr (packed structure)
+ # - msg_type
+ # - flags
+ # - padding (2 spare bytes)
+ # - ... payload ...
- def __init__(self, msg_type, flags = 0x00):
- # Init L1CTL message header
- self.data = struct.pack("BBxx", msg_type, flags)
+ def __init__(self, msg_type, flags=0x00):
+ # Init L1CTL message header
+ self.data = struct.pack("BBxx", msg_type, flags)
- def gen_msg(self):
- return struct.pack("!H", len(self.data)) + self.data
+ def gen_msg(self):
+ return struct.pack("!H", len(self.data)) + self.data
+
class L1CTLMessageReset(L1CTLMessage):
- # L1CTL message types
- L1CTL_RESET_REQ = 0x0d
- L1CTL_RESET_IND = 0x07
- L1CTL_RESET_CONF = 0x0e
+ # L1CTL message types
+ L1CTL_RESET_REQ = 0x0d
+ L1CTL_RESET_IND = 0x07
+ L1CTL_RESET_CONF = 0x0e
- # Reset types
- L1CTL_RES_T_BOOT = 0x00
- L1CTL_RES_T_FULL = 0x01
- L1CTL_RES_T_SCHED = 0x02
+ # Reset types
+ L1CTL_RES_T_BOOT = 0x00
+ L1CTL_RES_T_FULL = 0x01
+ L1CTL_RES_T_SCHED = 0x02
- def __init__(self, type = L1CTL_RES_T_FULL):
- super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
- self.data += struct.pack("Bxxx", type)
+ def __init__(self, type=L1CTL_RES_T_FULL):
+ super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
+ self.data += struct.pack("Bxxx", type)
+
class L1CTLMessageSIM(L1CTLMessage):
- # SIM related message types
- L1CTL_SIM_REQ = 0x16
- L1CTL_SIM_CONF = 0x17
+ # SIM related message types
+ L1CTL_SIM_REQ = 0x16
+ L1CTL_SIM_CONF = 0x17
- def __init__(self, pdu):
- super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
- self.data += pdu
+ def __init__(self, pdu):
+ super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
+ self.data += pdu
+
class CalypsoSimLink(LinkBase):
- """Transport Link for Calypso based phones."""
+ """Transport Link for Calypso based phones."""
- def __init__(self, sock_path:str = "/tmp/osmocom_l2", **kwargs):
- super().__init__(**kwargs)
- # Make sure that a given socket path exists
- if not os.path.exists(sock_path):
- raise ReaderError("There is no such ('%s') UNIX socket" % sock_path)
+ def __init__(self, sock_path: str = "/tmp/osmocom_l2", **kwargs):
+ super().__init__(**kwargs)
+ # Make sure that a given socket path exists
+ if not os.path.exists(sock_path):
+ raise ReaderError(
+ "There is no such ('%s') UNIX socket" % sock_path)
- print("Connecting to osmocon at '%s'..." % sock_path)
+ print("Connecting to osmocon at '%s'..." % sock_path)
- # Establish a client connection
- self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- self.sock.connect(sock_path)
+ # Establish a client connection
+ self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.sock.connect(sock_path)
- def __del__(self):
- self.sock.close()
+ def __del__(self):
+ self.sock.close()
- def wait_for_rsp(self, exp_len = 128):
- # Wait for incoming data (timeout is 3 seconds)
- s, _, _ = select.select([self.sock], [], [], 3.0)
- if not s:
- raise ReaderError("Timeout waiting for card response")
+ def wait_for_rsp(self, exp_len=128):
+ # Wait for incoming data (timeout is 3 seconds)
+ s, _, _ = select.select([self.sock], [], [], 3.0)
+ if not s:
+ raise ReaderError("Timeout waiting for card response")
- # Receive expected amount of bytes from osmocon
- rsp = self.sock.recv(exp_len)
- return rsp
+ # Receive expected amount of bytes from osmocon
+ rsp = self.sock.recv(exp_len)
+ return rsp
- def reset_card(self):
- # Request FULL reset
- req_msg = L1CTLMessageReset()
- self.sock.send(req_msg.gen_msg())
+ def reset_card(self):
+ # Request FULL reset
+ req_msg = L1CTLMessageReset()
+ self.sock.send(req_msg.gen_msg())
- # Wait for confirmation
- rsp = self.wait_for_rsp()
- rsp_msg = struct.unpack_from("!HB", rsp)
- if rsp_msg[1] != L1CTLMessageReset.L1CTL_RESET_CONF:
- raise ReaderError("Failed to reset Calypso PHY")
+ # Wait for confirmation
+ rsp = self.wait_for_rsp()
+ rsp_msg = struct.unpack_from("!HB", rsp)
+ if rsp_msg[1] != L1CTLMessageReset.L1CTL_RESET_CONF:
+ raise ReaderError("Failed to reset Calypso PHY")
- def connect(self):
- self.reset_card()
+ def connect(self):
+ self.reset_card()
- def disconnect(self):
- pass # Nothing to do really ...
+ def disconnect(self):
+ pass # Nothing to do really ...
- def wait_for_card(self, timeout = None, newcardonly = False):
- pass # Nothing to do really ...
+ def wait_for_card(self, timeout=None, newcardonly=False):
+ pass # Nothing to do really ...
- def _send_apdu_raw(self, pdu):
+ def _send_apdu_raw(self, pdu):
- # Request FULL reset
- req_msg = L1CTLMessageSIM(h2b(pdu))
- self.sock.send(req_msg.gen_msg())
+ # Request FULL reset
+ req_msg = L1CTLMessageSIM(h2b(pdu))
+ self.sock.send(req_msg.gen_msg())
- # Read message length first
- rsp = self.wait_for_rsp(struct.calcsize("!H"))
- msg_len = struct.unpack_from("!H", rsp)[0]
- if msg_len < struct.calcsize("BBxx"):
- raise ReaderError("Missing L1CTL header for L1CTL_SIM_CONF")
+ # Read message length first
+ rsp = self.wait_for_rsp(struct.calcsize("!H"))
+ msg_len = struct.unpack_from("!H", rsp)[0]
+ if msg_len < struct.calcsize("BBxx"):
+ raise ReaderError("Missing L1CTL header for L1CTL_SIM_CONF")
- # Read the whole message then
- rsp = self.sock.recv(msg_len)
+ # Read the whole message then
+ rsp = self.sock.recv(msg_len)
- # Verify L1CTL header
- hdr = struct.unpack_from("BBxx", rsp)
- if hdr[0] != L1CTLMessageSIM.L1CTL_SIM_CONF:
- raise ReaderError("Unexpected L1CTL message received")
+ # Verify L1CTL header
+ hdr = struct.unpack_from("BBxx", rsp)
+ if hdr[0] != L1CTLMessageSIM.L1CTL_SIM_CONF:
+ raise ReaderError("Unexpected L1CTL message received")
- # Verify the payload length
- offset = struct.calcsize("BBxx")
- if len(rsp) <= offset:
- raise ProtocolError("Empty response from SIM?!?")
+ # Verify the payload length
+ offset = struct.calcsize("BBxx")
+ if len(rsp) <= offset:
+ raise ProtocolError("Empty response from SIM?!?")
- # Omit L1CTL header
- rsp = rsp[offset:]
+ # Omit L1CTL header
+ rsp = rsp[offset:]
- # Unpack data and SW
- data = rsp[:-2]
- sw = rsp[-2:]
+ # Unpack data and SW
+ data = rsp[:-2]
+ sw = rsp[-2:]
- return b2h(data), b2h(sw)
+ return b2h(data), b2h(sw)
diff --git a/pySim/transport/modem_atcmd.py b/pySim/transport/modem_atcmd.py
index 04f9221..a373d68 100644
--- a/pySim/transport/modem_atcmd.py
+++ b/pySim/transport/modem_atcmd.py
@@ -27,138 +27,142 @@
# HACK: if somebody needs to debug this thing
# log.root.setLevel(log.DEBUG)
+
class ModemATCommandLink(LinkBase):
- """Transport Link for 3GPP TS 27.007 compliant modems."""
- def __init__(self, device:str='/dev/ttyUSB0', baudrate:int=115200, **kwargs):
- super().__init__(**kwargs)
- self._sl = serial.Serial(device, baudrate, timeout=5)
- self._echo = False # this will be auto-detected by _check_echo()
- self._device = device
- self._atr = None
+ """Transport Link for 3GPP TS 27.007 compliant modems."""
- # Check the AT interface
- self._check_echo()
+ def __init__(self, device: str = '/dev/ttyUSB0', baudrate: int = 115200, **kwargs):
+ super().__init__(**kwargs)
+ self._sl = serial.Serial(device, baudrate, timeout=5)
+ self._echo = False # this will be auto-detected by _check_echo()
+ self._device = device
+ self._atr = None
- # Trigger initial reset
- self.reset_card()
+ # Check the AT interface
+ self._check_echo()
- def __del__(self):
- if hasattr(self, '_sl'):
- self._sl.close()
+ # Trigger initial reset
+ self.reset_card()
- def send_at_cmd(self, cmd, timeout=0.2, patience=0.002):
- # Convert from string to bytes, if needed
- bcmd = cmd if type(cmd) is bytes else cmd.encode()
- bcmd += b'\r'
+ def __del__(self):
+ if hasattr(self, '_sl'):
+ self._sl.close()
- # Clean input buffer from previous/unexpected data
- self._sl.reset_input_buffer()
+ def send_at_cmd(self, cmd, timeout=0.2, patience=0.002):
+ # Convert from string to bytes, if needed
+ bcmd = cmd if type(cmd) is bytes else cmd.encode()
+ bcmd += b'\r'
- # Send command to the modem
- log.debug('Sending AT command: %s', cmd)
- try:
- wlen = self._sl.write(bcmd)
- assert(wlen == len(bcmd))
- except:
- raise ReaderError('Failed to send AT command: %s' % cmd)
+ # Clean input buffer from previous/unexpected data
+ self._sl.reset_input_buffer()
- rsp = b''
- its = 1
- t_start = time.time()
- while True:
- rsp = rsp + self._sl.read(self._sl.in_waiting)
- lines = rsp.split(b'\r\n')
- if len(lines) >= 2:
- res = lines[-2]
- if res == b'OK':
- log.debug('Command finished with result: %s', res)
- break
- if res == b'ERROR' or res.startswith(b'+CME ERROR:'):
- log.error('Command failed with result: %s', res)
- break
+ # Send command to the modem
+ log.debug('Sending AT command: %s', cmd)
+ try:
+ wlen = self._sl.write(bcmd)
+ assert(wlen == len(bcmd))
+ except:
+ raise ReaderError('Failed to send AT command: %s' % cmd)
- if time.time() - t_start >= timeout:
- log.info('Command finished with timeout >= %ss', timeout)
- break
- time.sleep(patience)
- its += 1
- log.debug('Command took %0.6fs (%d cycles a %fs)', time.time() - t_start, its, patience)
+ rsp = b''
+ its = 1
+ t_start = time.time()
+ while True:
+ rsp = rsp + self._sl.read(self._sl.in_waiting)
+ lines = rsp.split(b'\r\n')
+ if len(lines) >= 2:
+ res = lines[-2]
+ if res == b'OK':
+ log.debug('Command finished with result: %s', res)
+ break
+ if res == b'ERROR' or res.startswith(b'+CME ERROR:'):
+ log.error('Command failed with result: %s', res)
+ break
- if self._echo:
- # Skip echo chars
- rsp = rsp[wlen:]
- rsp = rsp.strip()
- rsp = rsp.split(b'\r\n\r\n')
+ if time.time() - t_start >= timeout:
+ log.info('Command finished with timeout >= %ss', timeout)
+ break
+ time.sleep(patience)
+ its += 1
+ log.debug('Command took %0.6fs (%d cycles a %fs)',
+ time.time() - t_start, its, patience)
- log.debug('Got response from modem: %s', rsp)
- return rsp
+ if self._echo:
+ # Skip echo chars
+ rsp = rsp[wlen:]
+ rsp = rsp.strip()
+ rsp = rsp.split(b'\r\n\r\n')
- def _check_echo(self):
- """Verify the correct response to 'AT' command
- and detect if inputs are echoed by the device
+ log.debug('Got response from modem: %s', rsp)
+ return rsp
- Although echo of inputs can be enabled/disabled via
- ATE1/ATE0, respectively, we rather detect the current
- configuration of the modem without any change.
- """
- # Next command shall not strip the echo from the response
- self._echo = False
- result = self.send_at_cmd('AT')
+ def _check_echo(self):
+ """Verify the correct response to 'AT' command
+ and detect if inputs are echoed by the device
- # Verify the response
- if len(result) > 0:
- if result[-1] == b'OK':
- self._echo = False
- return
- elif result[-1] == b'AT\r\r\nOK':
- self._echo = True
- return
- raise ReaderError('Interface \'%s\' does not respond to \'AT\' command' % self._device)
+ Although echo of inputs can be enabled/disabled via
+ ATE1/ATE0, respectively, we rather detect the current
+ configuration of the modem without any change.
+ """
+ # Next command shall not strip the echo from the response
+ self._echo = False
+ result = self.send_at_cmd('AT')
- def reset_card(self):
- # Reset the modem, just to be sure
- if self.send_at_cmd('ATZ') != [b'OK']:
- raise ReaderError('Failed to reset the modem')
+ # Verify the response
+ if len(result) > 0:
+ if result[-1] == b'OK':
+ self._echo = False
+ return
+ elif result[-1] == b'AT\r\r\nOK':
+ self._echo = True
+ return
+ raise ReaderError(
+ 'Interface \'%s\' does not respond to \'AT\' command' % self._device)
- # Make sure that generic SIM access is supported
- if self.send_at_cmd('AT+CSIM=?') != [b'OK']:
- raise ReaderError('The modem does not seem to support SIM access')
+ def reset_card(self):
+ # Reset the modem, just to be sure
+ if self.send_at_cmd('ATZ') != [b'OK']:
+ raise ReaderError('Failed to reset the modem')
- log.info('Modem at \'%s\' is ready!' % self._device)
+ # Make sure that generic SIM access is supported
+ if self.send_at_cmd('AT+CSIM=?') != [b'OK']:
+ raise ReaderError('The modem does not seem to support SIM access')
- def connect(self):
- pass # Nothing to do really ...
+ log.info('Modem at \'%s\' is ready!' % self._device)
- def disconnect(self):
- pass # Nothing to do really ...
+ def connect(self):
+ pass # Nothing to do really ...
- def wait_for_card(self, timeout=None, newcardonly=False):
- pass # Nothing to do really ...
+ def disconnect(self):
+ pass # Nothing to do really ...
- def _send_apdu_raw(self, pdu):
- # Make sure pdu has upper case hex digits [A-F]
- pdu = pdu.upper()
+ def wait_for_card(self, timeout=None, newcardonly=False):
+ pass # Nothing to do really ...
- # Prepare the command as described in 8.17
- cmd = 'AT+CSIM=%d,\"%s\"' % (len(pdu), pdu)
- log.debug('Sending command: %s', cmd)
+ def _send_apdu_raw(self, pdu):
+ # Make sure pdu has upper case hex digits [A-F]
+ pdu = pdu.upper()
- # Send AT+CSIM command to the modem
- # TODO: also handle +CME ERROR: <err>
- rsp = self.send_at_cmd(cmd)
- if len(rsp) != 2 or rsp[-1] != b'OK':
- raise ReaderError('APDU transfer failed: %s' % str(rsp))
- rsp = rsp[0] # Get rid of b'OK'
+ # Prepare the command as described in 8.17
+ cmd = 'AT+CSIM=%d,\"%s\"' % (len(pdu), pdu)
+ log.debug('Sending command: %s', cmd)
- # Make sure that the response has format: b'+CSIM: %d,\"%s\"'
- try:
- result = re.match(b'\+CSIM: (\d+),\"([0-9A-F]+)\"', rsp)
- (rsp_pdu_len, rsp_pdu) = result.groups()
- except:
- raise ReaderError('Failed to parse response from modem: %s' % rsp)
+ # Send AT+CSIM command to the modem
+ # TODO: also handle +CME ERROR: <err>
+ rsp = self.send_at_cmd(cmd)
+ if len(rsp) != 2 or rsp[-1] != b'OK':
+ raise ReaderError('APDU transfer failed: %s' % str(rsp))
+ rsp = rsp[0] # Get rid of b'OK'
- # TODO: make sure we have at least SW
- data = rsp_pdu[:-4].decode().lower()
- sw = rsp_pdu[-4:].decode().lower()
- log.debug('Command response: %s, %s', data, sw)
- return data, sw
+ # Make sure that the response has format: b'+CSIM: %d,\"%s\"'
+ try:
+ result = re.match(b'\+CSIM: (\d+),\"([0-9A-F]+)\"', rsp)
+ (rsp_pdu_len, rsp_pdu) = result.groups()
+ except:
+ raise ReaderError('Failed to parse response from modem: %s' % rsp)
+
+ # TODO: make sure we have at least SW
+ data = rsp_pdu[:-4].decode().lower()
+ sw = rsp_pdu[-4:].decode().lower()
+ log.debug('Command response: %s, %s', data, sw)
+ return data, sw
diff --git a/pySim/transport/pcsc.py b/pySim/transport/pcsc.py
index 147a6b7..ca1751a 100644
--- a/pySim/transport/pcsc.py
+++ b/pySim/transport/pcsc.py
@@ -28,63 +28,64 @@
class PcscSimLink(LinkBase):
- """ pySim: PCSC reader transport link."""
+ """ pySim: PCSC reader transport link."""
- def __init__(self, reader_number:int=0, **kwargs):
- super().__init__(**kwargs)
- r = readers()
- if reader_number >= len(r):
- raise ReaderError
- self._reader = r[reader_number]
- self._con = self._reader.createConnection()
+ def __init__(self, reader_number: int = 0, **kwargs):
+ super().__init__(**kwargs)
+ r = readers()
+ if reader_number >= len(r):
+ raise ReaderError
+ self._reader = r[reader_number]
+ self._con = self._reader.createConnection()
- def __del__(self):
- try:
- # FIXME: this causes multiple warnings in Python 3.5.3
- self._con.disconnect()
- except:
- pass
- return
+ def __del__(self):
+ try:
+ # FIXME: this causes multiple warnings in Python 3.5.3
+ self._con.disconnect()
+ except:
+ pass
+ return
- def wait_for_card(self, timeout:int=None, newcardonly:bool=False):
- cr = CardRequest(readers=[self._reader], timeout=timeout, newcardonly=newcardonly)
- try:
- cr.waitforcard()
- except CardRequestTimeoutException:
- raise NoCardError()
- self.connect()
+ def wait_for_card(self, timeout: int = None, newcardonly: bool = False):
+ cr = CardRequest(readers=[self._reader],
+ timeout=timeout, newcardonly=newcardonly)
+ try:
+ cr.waitforcard()
+ except CardRequestTimeoutException:
+ raise NoCardError()
+ self.connect()
- def connect(self):
- try:
- # To avoid leakage of resources, make sure the reader
- # is disconnected
- self.disconnect()
+ def connect(self):
+ try:
+ # To avoid leakage of resources, make sure the reader
+ # is disconnected
+ self.disconnect()
- # Explicitly select T=0 communication protocol
- self._con.connect(CardConnection.T0_protocol)
- except CardConnectionException:
- raise ProtocolError()
- except NoCardException:
- raise NoCardError()
+ # Explicitly select T=0 communication protocol
+ self._con.connect(CardConnection.T0_protocol)
+ except CardConnectionException:
+ raise ProtocolError()
+ except NoCardException:
+ raise NoCardError()
- def get_atr(self):
- return self._con.getATR()
+ def get_atr(self):
+ return self._con.getATR()
- def disconnect(self):
- self._con.disconnect()
+ def disconnect(self):
+ self._con.disconnect()
- def reset_card(self):
- self.disconnect()
- self.connect()
- return 1
+ def reset_card(self):
+ self.disconnect()
+ self.connect()
+ return 1
- def _send_apdu_raw(self, pdu):
+ def _send_apdu_raw(self, pdu):
- apdu = h2i(pdu)
+ apdu = h2i(pdu)
- data, sw1, sw2 = self._con.transmit(apdu)
+ data, sw1, sw2 = self._con.transmit(apdu)
- sw = [sw1, sw2]
+ sw = [sw1, sw2]
- # Return value
- return i2h(data), i2h(sw)
+ # Return value
+ return i2h(data), i2h(sw)
diff --git a/pySim/transport/serial.py b/pySim/transport/serial.py
index c080926..924df9e 100644
--- a/pySim/transport/serial.py
+++ b/pySim/transport/serial.py
@@ -26,209 +26,211 @@
class SerialSimLink(LinkBase):
- """ pySim: Transport Link for serial (RS232) based readers included with simcard"""
+ """ pySim: Transport Link for serial (RS232) based readers included with simcard"""
- def __init__(self, device:str='/dev/ttyUSB0', baudrate:int=9600, rst:str='-rts',
- debug:bool=False, **kwargs):
- super().__init__(**kwargs)
- if not os.path.exists(device):
- raise ValueError("device file %s does not exist -- abort" % device)
- self._sl = serial.Serial(
- port = device,
- parity = serial.PARITY_EVEN,
- bytesize = serial.EIGHTBITS,
- stopbits = serial.STOPBITS_TWO,
- timeout = 1,
- xonxoff = 0,
- rtscts = 0,
- baudrate = baudrate,
- )
- self._rst_pin = rst
- self._debug = debug
- self._atr = None
+ def __init__(self, device: str = '/dev/ttyUSB0', baudrate: int = 9600, rst: str = '-rts',
+ debug: bool = False, **kwargs):
+ super().__init__(**kwargs)
+ if not os.path.exists(device):
+ raise ValueError("device file %s does not exist -- abort" % device)
+ self._sl = serial.Serial(
+ port=device,
+ parity=serial.PARITY_EVEN,
+ bytesize=serial.EIGHTBITS,
+ stopbits=serial.STOPBITS_TWO,
+ timeout=1,
+ xonxoff=0,
+ rtscts=0,
+ baudrate=baudrate,
+ )
+ self._rst_pin = rst
+ self._debug = debug
+ self._atr = None
- def __del__(self):
- if (hasattr(self, "_sl")):
- self._sl.close()
+ def __del__(self):
+ if (hasattr(self, "_sl")):
+ self._sl.close()
- def wait_for_card(self, timeout=None, newcardonly=False):
- # Direct try
- existing = False
+ def wait_for_card(self, timeout=None, newcardonly=False):
+ # Direct try
+ existing = False
- try:
- self.reset_card()
- if not newcardonly:
- return
- else:
- existing = True
- except NoCardError:
- pass
+ try:
+ self.reset_card()
+ if not newcardonly:
+ return
+ else:
+ existing = True
+ except NoCardError:
+ pass
- # Poll ...
- mt = time.time() + timeout if timeout is not None else None
- pe = 0
+ # Poll ...
+ mt = time.time() + timeout if timeout is not None else None
+ pe = 0
- while (mt is None) or (time.time() < mt):
- try:
- time.sleep(0.5)
- self.reset_card()
- if not existing:
- return
- except NoCardError:
- existing = False
- except ProtocolError:
- if existing:
- existing = False
- else:
- # Tolerate a couple of protocol error ... can happen if
- # we try when the card is 'half' inserted
- pe += 1
- if (pe > 2):
- raise
+ while (mt is None) or (time.time() < mt):
+ try:
+ time.sleep(0.5)
+ self.reset_card()
+ if not existing:
+ return
+ except NoCardError:
+ existing = False
+ except ProtocolError:
+ if existing:
+ existing = False
+ else:
+ # Tolerate a couple of protocol error ... can happen if
+ # we try when the card is 'half' inserted
+ pe += 1
+ if (pe > 2):
+ raise
- # Timed out ...
- raise NoCardError()
+ # Timed out ...
+ raise NoCardError()
- def connect(self):
- self.reset_card()
+ def connect(self):
+ self.reset_card()
- def get_atr(self):
- return self._atr
+ def get_atr(self):
+ return self._atr
- def disconnect(self):
- pass # Nothing to do really ...
+ def disconnect(self):
+ pass # Nothing to do really ...
- def reset_card(self):
- rv = self._reset_card()
- if rv == 0:
- raise NoCardError()
- elif rv < 0:
- raise ProtocolError()
+ def reset_card(self):
+ rv = self._reset_card()
+ if rv == 0:
+ raise NoCardError()
+ elif rv < 0:
+ raise ProtocolError()
- def _reset_card(self):
- self._atr = None
- rst_meth_map = {
- 'rts': self._sl.setRTS,
- 'dtr': self._sl.setDTR,
- }
- rst_val_map = { '+':0, '-':1 }
+ def _reset_card(self):
+ self._atr = None
+ rst_meth_map = {
+ 'rts': self._sl.setRTS,
+ 'dtr': self._sl.setDTR,
+ }
+ rst_val_map = {'+': 0, '-': 1}
- try:
- rst_meth = rst_meth_map[self._rst_pin[1:]]
- rst_val = rst_val_map[self._rst_pin[0]]
- except:
- raise ValueError('Invalid reset pin %s' % self._rst_pin)
+ try:
+ rst_meth = rst_meth_map[self._rst_pin[1:]]
+ rst_val = rst_val_map[self._rst_pin[0]]
+ except:
+ raise ValueError('Invalid reset pin %s' % self._rst_pin)
- rst_meth(rst_val)
- time.sleep(0.1) # 100 ms
- self._sl.flushInput()
- rst_meth(rst_val ^ 1)
+ rst_meth(rst_val)
+ time.sleep(0.1) # 100 ms
+ self._sl.flushInput()
+ rst_meth(rst_val ^ 1)
- b = self._rx_byte()
- if not b:
- return 0
- if ord(b) != 0x3b:
- return -1
- self._dbg_print("TS: 0x%x Direct convention" % ord(b))
+ b = self._rx_byte()
+ if not b:
+ return 0
+ if ord(b) != 0x3b:
+ return -1
+ self._dbg_print("TS: 0x%x Direct convention" % ord(b))
- while ord(b) == 0x3b:
- b = self._rx_byte()
+ while ord(b) == 0x3b:
+ b = self._rx_byte()
- if not b:
- return -1
- t0 = ord(b)
- self._dbg_print("T0: 0x%x" % t0)
- self._atr = [0x3b, ord(b)]
+ if not b:
+ return -1
+ t0 = ord(b)
+ self._dbg_print("T0: 0x%x" % t0)
+ self._atr = [0x3b, ord(b)]
- for i in range(4):
- if t0 & (0x10 << i):
- b = self._rx_byte()
- self._atr.append(ord(b))
- self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
+ for i in range(4):
+ if t0 & (0x10 << i):
+ b = self._rx_byte()
+ self._atr.append(ord(b))
+ self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
- for i in range(0, t0 & 0xf):
- b = self._rx_byte()
- self._atr.append(ord(b))
- self._dbg_print("Historical = %x" % ord(b))
+ for i in range(0, t0 & 0xf):
+ b = self._rx_byte()
+ self._atr.append(ord(b))
+ self._dbg_print("Historical = %x" % ord(b))
- while True:
- x = self._rx_byte()
- if not x:
- break
- self._atr.append(ord(x))
- self._dbg_print("Extra: %x" % ord(x))
+ while True:
+ x = self._rx_byte()
+ if not x:
+ break
+ self._atr.append(ord(x))
+ self._dbg_print("Extra: %x" % ord(x))
- return 1
+ return 1
- def _dbg_print(self, s):
- if self._debug:
- print(s)
+ def _dbg_print(self, s):
+ if self._debug:
+ print(s)
- def _tx_byte(self, b):
- self._sl.write(b)
- r = self._sl.read()
- if r != b: # TX and RX are tied, so we must clear the echo
- raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (ord(b), '%02x'%ord(r) if r else '(nil)'))
+ def _tx_byte(self, b):
+ self._sl.write(b)
+ r = self._sl.read()
+ if r != b: # TX and RX are tied, so we must clear the echo
+ raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (
+ ord(b), '%02x' % ord(r) if r else '(nil)'))
- def _tx_string(self, s):
- """This is only safe if it's guaranteed the card won't send any data
- during the time of tx of the string !!!"""
- self._sl.write(s)
- r = self._sl.read(len(s))
- if r != s: # TX and RX are tied, so we must clear the echo
- raise ProtocolError("Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
+ def _tx_string(self, s):
+ """This is only safe if it's guaranteed the card won't send any data
+ during the time of tx of the string !!!"""
+ self._sl.write(s)
+ r = self._sl.read(len(s))
+ if r != s: # TX and RX are tied, so we must clear the echo
+ raise ProtocolError(
+ "Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
- def _rx_byte(self):
- return self._sl.read()
+ def _rx_byte(self):
+ return self._sl.read()
- def _send_apdu_raw(self, pdu):
+ def _send_apdu_raw(self, pdu):
- pdu = h2b(pdu)
- data_len = pdu[4] # P3
+ pdu = h2b(pdu)
+ data_len = pdu[4] # P3
- # Send first CLASS,INS,P1,P2,P3
- self._tx_string(pdu[0:5])
+ # Send first CLASS,INS,P1,P2,P3
+ self._tx_string(pdu[0:5])
- # Wait ack which can be
- # - INS: Command acked -> go ahead
- # - 0x60: NULL, just wait some more
- # - SW1: The card can apparently proceed ...
- while True:
- b = self._rx_byte()
- if ord(b) == pdu[1]:
- break
- elif b != '\x60':
- # Ok, it 'could' be SW1
- sw1 = b
- sw2 = self._rx_byte()
- nil = self._rx_byte()
- if (sw2 and not nil):
- return '', b2h(sw1+sw2)
+ # Wait ack which can be
+ # - INS: Command acked -> go ahead
+ # - 0x60: NULL, just wait some more
+ # - SW1: The card can apparently proceed ...
+ while True:
+ b = self._rx_byte()
+ if ord(b) == pdu[1]:
+ break
+ elif b != '\x60':
+ # Ok, it 'could' be SW1
+ sw1 = b
+ sw2 = self._rx_byte()
+ nil = self._rx_byte()
+ if (sw2 and not nil):
+ return '', b2h(sw1+sw2)
- raise ProtocolError()
+ raise ProtocolError()
- # Send data (if any)
- if len(pdu) > 5:
- self._tx_string(pdu[5:])
+ # Send data (if any)
+ if len(pdu) > 5:
+ self._tx_string(pdu[5:])
- # Receive data (including SW !)
- # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
- to_recv = data_len - len(pdu) + 5 + 2
+ # Receive data (including SW !)
+ # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
+ to_recv = data_len - len(pdu) + 5 + 2
- data = bytes(0)
- while (len(data) < to_recv):
- b = self._rx_byte()
- if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
- continue
- if not b:
- break
- data += b
+ data = bytes(0)
+ while (len(data) < to_recv):
+ b = self._rx_byte()
+ if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
+ continue
+ if not b:
+ break
+ data += b
- # Split datafield from SW
- if len(data) < 2:
- return None, None
- sw = data[-2:]
- data = data[0:-2]
+ # Split datafield from SW
+ if len(data) < 2:
+ return None, None
+ sw = data[-2:]
+ data = data[0:-2]
- # Return value
- return b2h(data), b2h(sw)
+ # Return value
+ return b2h(data), b2h(sw)