Introduction and Tutorial ========================= .. highlightlang:: python3 .. _simple_examle: A SCardFace primer ------------------ here is a short example, highlighting some of the features of SCardFace: :: from scardface import SCard, readers, C_APDU, R_APDU print("My readers are {0}".format(readers())) print("Insert a smart card in any reader") h = SCard( ) # wait forever until a card # is inserted in any reader # or Ctrl-C hit print(h.atr) # print card ATR (bytes) select_rootdir = C_APDU( ins=0x00, cla=0xa4, data=b"\x3f\x00" ) response = h.send( select_rootdir ) if not response.is_successful(): print("The card returned this error code: {0}"format(response.ashex())) data,sw12 = response.data, response.sw12 print("Please remove card") h.wait() # wait for removal The first function call, :func:`readers()`, is used to fetch the list of available readers on the system. it returns a tuple of unicode strings containing reader names.:: print("My readers are {0}".format(readers())) :func:`readers()` optionally supports a group name as argument, see :ref:`library_reference` for more details. Also, There is :func:`groups()` function, that can be to retrieve groups of readers. Obtaining a smartcard interface ------------------------------- Then, the :class:`SCard` constructor is called, with no parameters.:: h = SCard( ) # wait forever until a card # is inserted in any reader # or Ctrl-C hit By default, the initializer will wait until a card insertion is detected on any available reader of the system, and then attempt to establish an exclusive connection with that card. Since, in this example, no timeout is given, it waits forever, unless :kbd:`CTRL-C` is hit, :meth:`SCard.cancel()` or :func:`cancel_all_requests()` being called from another thread. :class:`SCard` instances have several properties and method, which are listed inside :ref:`library_reference`. In this example, the Answer To Reset (``ATR``) bytes are available from the ``atr`` property: :: print(h.atr) # print card ATR (bytes) Once a valid connection is established with a card, one can send commands and receive responses to/from the card. The library attempts to hide the gory details of T=0 and T=1 protocols, and is proposing a consistent API and object collection to talk with the cards, regardless of the underlying protocol. Building a command APDU ----------------------- One of the easiest way to build a request is through the class method ``C_APDU.fromhex()``, which accepts a hex string of a valid APDU command. :: select_rootdir = C_APDU.fromhex("00 A4 02 00 02 3F 00") The ``C_APDU.fromhex()`` class method call will attempt to build a ``C_APDU`` instance from the hex string it is given as an argument. It can deal with ISO long and short cases. Please note however that you sometimes need to explicitely add the ``le`` byte if the command is Case 3 or 4 (in other word, when an answer is expected). That knowledge varies from command to command and even sometimes from implementation to implementation. The regular way to build a ``C_APDU`` object is by invoking its initializer:: my_command = C_APDU( 0x00, 0xA4, data=b'\x3F\x00') The two first arguments are mandatory. They represent the class and intruction of the command. If a data field is given, the command see its ``lc`` parameter adjusted to reflect the lenght of the data. By default, no data is expected, i.e. the ISO case is either 1 or 3. To force a length on the return, specify the expected length through ``le``:: my_command = C_APDU( 0x00, 0xA4, data=b'\x3F\x00', le=8) # we expect 8 bytes Alternatively, the expected length can be specified afterwards:: my_command = C_APDU( 0x00, 0xA4, data=b'\x3F\x00') my_command.le = 8 # expecting exactly 8 bytes If the expected length is unknown, the value of ``le`` shall be set to ``-1``:: my_command = C_APDU( 0x00, 0xA4, data=b'\x3F\x00') my_command.le = -1 # expecting data, let SCard() object deal # with actual length received In such case, the ``SCard.send()`` method is taking care of determining how much data will be returned. see **REF** for more details. .. _sending_receiving: sending command and dealing with the response --------------------------------------------- Once created, the ``C_APDU`` instance can be tweaked through its properties, see **REF**. Then, the instance can be directly used as an argument to the ``send`` method of the ``SCard`` instance:: response = h.send( select_rootdir ) print("Response back is {0}"format(response.ashex())) data,sw12 = response.data, response.sw12 This command will deal with TPDU and specific ISO commands like ``GET RESPONSE`` and ``GET ENVELOPPE`` when necessary, it is therefore no more needed to manage the TPDU layer. This call will return an ``R_APDU`` instance, whose properties can be accessed for further processing. ``R_APDU`` instances have several methods and properties, and amongst them the ``is_successful()`` returns ``True`` if the R-APDU response code equals ``b"\x90\x00"`` (in hexadecimal). Finally, the card removal can be watched through calling ``wait()`` method. This method accepts optionally one parameter, which is a timeout after which it returns a ``SCardError()`` timeout event, see **REF** for more details. advanced use of SCard --------------------- Often, smart card commands are chained, i.e. several commands must be sent to implement a functionality. The way to do this is :: h = SCard() r = h.send(command1) if not r.is_successful(): raise RuntimeError("command 1 did not succeed") r = h.send(command2) if not r.is_successful(): raise RuntimeError("command 2 did not succeed") r = h.send(command2) if not r.is_successful(): raise RuntimeError("command 3 did not succeed") There is a quicker way to formulate this: the ``send`` method can receive an list of commands to send, instead of just one :: h = SCard() r = send( command1, command2, command3) By default, commands are sent until one of the R-APDU is not successful, or until the list is exhausted. As a drawback, this way does not return the offending command. Hopefully, it is possible to provide ``send`` with a callback. :: def my_callback(cmd, resp): """ callback function for chained sending. """ if not resp.is_successful(): raise RuntimeError("offendig command:{0}\nresponse is {1}".format(cmd.ashex(), resp.ashex())) return True h = SCard() r = send( command1, command2, command3, callback=my_callback) The callback function, if provided, receives two arguments - the first argument is the ``C_APDU`` instance being sent to the card - the second argument is the ``R_APDU`` instance returned by the card This callback is called after each call to ``SCard.send()``. If it returns ``True``, the call proceeds to the next command in the chain.