From: <lu...@us...> - 2014-12-11 11:22:26
|
Revision: 649 http://sourceforge.net/p/pyscard/code/649 Author: ludov Date: 2014-12-11 11:22:22 +0000 (Thu, 11 Dec 2014) Log Message: ----------- Add documentation in Sphinx format Added Paths: ----------- trunk/pyscard/src/smartcard/doc/Makefile trunk/pyscard/src/smartcard/doc/conf.py trunk/pyscard/src/smartcard/doc/index.rst trunk/pyscard/src/smartcard/doc/license.rst trunk/pyscard/src/smartcard/doc/main.rst trunk/pyscard/src/smartcard/doc/pyscard-framework.rst trunk/pyscard/src/smartcard/doc/pyscard-wrapper.rst trunk/pyscard/src/smartcard/doc/user-guide.rst Added: trunk/pyscard/src/smartcard/doc/Makefile =================================================================== --- trunk/pyscard/src/smartcard/doc/Makefile (rev 0) +++ trunk/pyscard/src/smartcard/doc/Makefile 2014-12-11 11:22:22 UTC (rev 649) @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make <target>' where <target> is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyscard.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyscard.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/pyscard" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyscard" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." Added: trunk/pyscard/src/smartcard/doc/conf.py =================================================================== --- trunk/pyscard/src/smartcard/doc/conf.py (rev 0) +++ trunk/pyscard/src/smartcard/doc/conf.py 2014-12-11 11:22:22 UTC (rev 649) @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- +# +# pyscard documentation build configuration file, created by +# sphinx-quickstart on Thu Dec 4 14:38:51 2014. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('../..')) +sys.path.insert(0, os.path.abspath('../../build/lib.linux-x86_64-2.7')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'pyscard' +copyright = u'2014, Jean-Daniel Aussel, Ludovic Rousseau' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.6.16' +# The full version, including alpha/beta/rc tags. +release = '1.6.16' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pyscarddoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'pyscard.tex', u'pyscard Documentation', + u'Ludovic Rousseau', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'pyscard', u'pyscard Documentation', + [u'Ludovic Rousseau'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'pyscard', u'pyscard Documentation', + u'Ludovic Rousseau', 'pyscard', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' Added: trunk/pyscard/src/smartcard/doc/index.rst =================================================================== --- trunk/pyscard/src/smartcard/doc/index.rst (rev 0) +++ trunk/pyscard/src/smartcard/doc/index.rst 2014-12-11 11:22:22 UTC (rev 649) @@ -0,0 +1,26 @@ +.. pyscard documentation master file, created by + sphinx-quickstart on Thu Dec 4 14:38:51 2014. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to pyscard's documentation! +=================================== + +Contents: + +.. toctree:: + :maxdepth: 4 + + main + user-guide + pyscard-framework + pyscard-wrapper + license + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + Added: trunk/pyscard/src/smartcard/doc/license.rst =================================================================== --- trunk/pyscard/src/smartcard/doc/license.rst (rev 0) +++ trunk/pyscard/src/smartcard/doc/license.rst 2014-12-11 11:22:22 UTC (rev 649) @@ -0,0 +1,18 @@ +License +####### + +pyscard is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. + +pyscard is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with pyscard; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +.. literalinclude:: ../LICENSE Added: trunk/pyscard/src/smartcard/doc/main.rst =================================================================== --- trunk/pyscard/src/smartcard/doc/main.rst (rev 0) +++ trunk/pyscard/src/smartcard/doc/main.rst 2014-12-11 11:22:22 UTC (rev 649) @@ -0,0 +1,42 @@ +pyscard - python for smart cards +################################ + +pyscard - python smart card library - is a python module adding smart +cards support to `python <http://www.python.org/>`_. + +`download <http://sourceforge.net/projects/pyscard/>`_ pyscard from sourceforge.net. + +Report bugs, patches and feature requests using the `sourceforge pyscard +bug tracking system <https://sourceforge.net/p/pyscard/_list/tickets>`_. + +Pyscard consists of: + +* `smartcard.scard + <http://pyscard.sourceforge.net/epydoc/smartcard.scard.scard-module.html>`_, + an extension module wrapping the WinSCard API (smart card base + components) also known as PC/SC, and + +* `smartcard <http://pyscard.sourceforge.net/epydoc/index.html>`_, a + higher level python framework built on top of the raw PC/SC API. + +.. image:: ../doc/images/pyscard.jpg + :align: center + +Documentation Index +******************* + + :ref:`pyscard_user_guide` + + `smartcard reference (python smart card library) + <http://pyscard.sourceforge.net/epydoc/index.html>`_ + + `scard reference (python PCSC wrapper) + <http://pyscard.sourceforge.net/epydoc/smartcard.scard.scard-module.html>`_, + the python wrapper around PCSC + +Samples +******* + + High level API samples: See :ref:`framework_samples` + + Low level API samples: See :ref:`wrapper_samples` Added: trunk/pyscard/src/smartcard/doc/pyscard-framework.rst =================================================================== --- trunk/pyscard/src/smartcard/doc/pyscard-framework.rst (rev 0) +++ trunk/pyscard/src/smartcard/doc/pyscard-framework.rst 2014-12-11 11:22:22 UTC (rev 649) @@ -0,0 +1,76 @@ +.. _framework_samples: + +pyscard smartcard framework samples +=================================== + +pyscard is a python module adding smart cards support to python. + +It consists of smartcard.scard, an extension module wrapping Windows +smart card base components (also known as PCSC), and smartcard, a python +framework library hiding PCSC complexity. + +Display the ATR of inserted cards +""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/simple/getATR.py + + +Selecting the DF_TELECOM of a card +"""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/simple/selectDF_TELECOM.py + + +A simple apdu tracer and interpreter +"""""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/framework/sample_apduTracerInterpreter.py + + +Tracing connection events +""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/framework/sample_ConsoleConnectionTracer.py + + +Decorating Card Connections to add custom behavior +"""""""""""""""""""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/framework/sample_CardConnectionDecorator.py + + +Detecting response apdu errors +"""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/framework/sample_ErrorChecking.py + + +Implementing a custom ErrorChecker +"""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/framework/sample_CustomErrorChecker.py + + +Implementing a custom card type +""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/framework/sample_CustomCardType.py + + +Monitoring smartcard readers +"""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/framework/sample_MonitorReaders.py + + +Monitoring smartcard insertion/removal +"""""""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/framework/sample_MonitorCards.py + + +APDU/ATR byte to string utilities +""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/framework/sample_toHexString.py + Added: trunk/pyscard/src/smartcard/doc/pyscard-wrapper.rst =================================================================== --- trunk/pyscard/src/smartcard/doc/pyscard-wrapper.rst (rev 0) +++ trunk/pyscard/src/smartcard/doc/pyscard-wrapper.rst 2014-12-11 11:22:22 UTC (rev 649) @@ -0,0 +1,80 @@ +.. _wrapper_samples: + +PCSC wrapper samples +==================== + +Using the smartcard framework is the preferred way to write python smart +card application. You can however use the smartcard.scard library to +write your python smart card application if you want to write your own +python framework, or if you want to access some features of the SCardXXX +C API not available in the smartcard framework. + +The smartcard.scard module is a native extension module wrapping Windows +smart card base components (also known as PCSC) on Windows, and +pcsc-lite on linux and Mac OS X, whereas the smartcard framework is a +pure python framework hiding scard complexity and PCSC. + +send a Control Code to a card or reader +""""""""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_control.py + + +get the ATR of a card +""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_getATR.py + + +get the attributes of a card +"""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_getAttrib.py + + +wait for card insertion/removal +""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_getStatusChange.py + + +list the cards introduced in the system +""""""""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_listCards.py + + +list the interfaces supported by a card +""""""""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_listInterfaces.py + + +locate cards in the system +"""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_locateCards.py + + +manage readers and reader groups +"""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_readerGroups.py + + +list smart card readers +""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_readers.py + + +select the DF_TELECOM of a SIM card +""""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_selectDFTelecom.py + + +perform a simple smart card transaction +""""""""""""""""""""""""""""""""""""""" + +.. literalinclude:: ../Examples/scard-api/sample_transaction.py Added: trunk/pyscard/src/smartcard/doc/user-guide.rst =================================================================== --- trunk/pyscard/src/smartcard/doc/user-guide.rst (rev 0) +++ trunk/pyscard/src/smartcard/doc/user-guide.rst 2014-12-11 11:22:22 UTC (rev 649) @@ -0,0 +1,1637 @@ +.. _pyscard_user_guide: + +pyscard user's guide +#################### + +Copyright +********* + +| Copyright 2001-2009 `Gemalto <http://www.gemalto.com/>`_ +| Author: Jean-Daniel Aussel, mailto:jea...@ge... + +This file is part of pyscard. + +pyscard is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. + +pyscard is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with pyscard; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Introduction +************ + +The pyscard smartcard library is a framework for building smart card +aware applications in Python. The smartcard module is built on top of +the PCSC API Python wrapper module. + +pyscard supports Windows 2000 and XP by using the `Microsoft Smart Card +Base <http://msdn2.microsoft.com/en-us/library/aa374731.aspx#smart_card_functions>`_ components, and linux and Mac OS X by using `PCSC-lite <http://pcsclite.alioth.debian.org/>`_. + + +Smart Cards +*********** + +Smart cards are plastic cards having generally the size of a credit card +and embedding a microprocessor. Smart cards communicate with the outside +world thru a serial port interface and an half-duplex protocol. +Smartcards usually interface with a dedicated terminal, such as a +point-of-sale terminal or a mobile phone. Sometime, smart cards have to +be interfaced with personal computers. This is the case for some +applications such as secure login, mail cyphering or digital signature, +but also for some PC based smart card tools used to personnalize or edit +the content of smart cards. Smart cards are interfaced with a personnal +computer using a smart card reader. The smart card reader connects on +one side to the serial port of the smart card, and on the other side to +the PC, often nowadays thru a USB port. + +The PCSC workgroup has defined a standard API to interface smart card +and smart card readers to a PC. The resulting reference implementation +on linux and Mac OS X operating systems is `PC/SC-lite +<http://pcsclite.alioth.debian.org/>`_. All windows operating systems +also include out of the box smart card support, usually called `PCSC +<http://msdn2.microsoft.com/en-us/library/aa374731.aspx#smart_card_functions>`_. + +The PCSC API is implemented in C language, and several bridges are +provided to access the PCSC API from different languages such as java or +visual basic. pyscard is a python framework to develop smart card PC +applications on linux, Mac OS X and windows. pyscard lower layers +interface to the PCSC API to access the smart cards and smart card +readers. + + +Quick-start +*********** + +We will see in this section some variations on how to send APDU commands +to a smart card. + + +The reader-centric approach +=========================== + +A PC application interacts with a card by sending list of bytes, known +as Application Protocol Data Units (APDU). The format of these APDUs is +defined in the ISO7816-4 standard. To send APDUs to a card, the +application needs first to connect to a card thru a smart card reader. +Smart card aware applications that first select a smart card reader, +then connect to the card inserted in the smart card reader use the +reader-centric approach. + +In the reader-centric approach, we open a connection with a card thru a +smart card reader, and send APDU commands to the card using the +connection: + +.. sourcecode:: python + + >>> from smartcard.System import readers + >>> from smartcard.util import toHexString + >>> + >>> r=readers() + >>> print r + ['SchlumbergerSema Reflex USB v.2 0', 'Utimaco CardManUSB 0'] + >>> connection = r[0].createConnection() + >>> connection.connect() + >>> SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + >>> DF_TELECOM = [0x7F, 0x10] + >>> data, sw1, sw2 = connection.transmit( SELECT + DF_TELECOM ) + >>> print "%x %x" % (sw1, sw2) + 9f 1a + >>> + +The list of available readers is retrieved with the readers() function. +We create a connection with the first reader (index 0 for reader 1, 1 +for reader 2, ...) with the r[0].createConnection() call and connect to +the card with the connect() method of the connection. We can then send +APDU commands to the card with the transmit() method. + +Scripts written with the reader centric approach however have the +following drawbacks: + +* the reader index or reader name is hardcoded in the scripts; the + scripts must be edited to match each user configuration; for example + in the previous script, we would have to edit the script and change + r[0] to r[1] for using the second reader + +* there is no a-priori knowledge that the card is in the reader; to + detect card insertion, we would have to execute the script and + eventually catch a CardConnectionException that would indicate that + there is no card in the reader. + +* there is no built-in check that the card in the reader is of the card + type we expect; in the previous example, we might try to select the + DF_TELECOM of an EMV card. + +Most of these issues are solved with the card-centric approach, based on +card type detection techniques, such as using the Answer To Reset (ATR) +of the card. + + +The Answer To Reset (ATR) +========================= + +The first answer of a smart card inserted in a smart card reader is call +the ATR. The purpose of the ATR is to describe the supported +communication parameters. The smart card reader, smart card reader +driver, and operating system will use these parameters to establish a +communication with the card. The ATR is described in the ISO7816-3 +standard. The first bytes of the ATR describe the voltage convention +(direct or inverse), followed by bytes describing the available +communication interfaces and their respective parameters. These +interface bytes are then followed by Historical Bytes which are not +standardized, and are useful for transmitting proprietary informations +such as the card type, the version of the embedded software, or the card +state. Finally these historical bytes are eventually followd by a +checksum byte. + +The class `smartcard.ATR +<http://pyscard.sourceforge.net/epydoc/smartcard.ATR.ATR-class.html>`_ +is a pyscard utility class that can interpret the content of an ATR: + +.. sourcecode:: python + + #! /usr/bin/env python + from smartcard.ATR import ATR + from smartcard.util import toHexString + + atr = ATR([0x3B, 0x9E, 0x95, 0x80, 0x1F, 0xC3, 0x80, 0x31, 0xA0, + 0x73, 0xBE, 0x21, 0x13, 0x67, 0x29, 0x02, 0x01, 0x01, 0x81,0xCD,0xB9] ) + print atr + print 'historical bytes: ', toHexString( atr.getHistoricalBytes() ) + print 'checksum:', "0x%X" % atr.getChecksum() + print 'checksum OK:', atr.checksumOK + print 'T0 supported:', atr.isT0Supported() + print 'T1 supported:', atr.isT1Supported() + print 'T15 supported:', atr.isT15Supported() + +Which results in the following output:: + + 3B 9E 95 80 1F C3 80 31 A0 73 BE 21 13 67 29 02 01 01 81 CD B9 + historical bytes: 80 31 A0 73 BE 21 13 67 29 02 01 01 81 CD + checksum: 0xB9 + checksum OK: True + T0 supported: True + T1 supported: False + T15 supported: True + +In practice, the ATR can be used to detect a particular card, either by +trying to match a card with a complete ATR, or by matching a card with +some data in the historical bytes. Smart card aware PC applications that +detects smart cards based on the content of the ATR use the card-centric +approach, independently on the smart card reader in which the card is +inserted.. + + +The card-centric approach +========================= + +In the card-centric approach, we create a request for a specific type of +card and wait until a card matching the request is inserted. Once a +matching card is introduced, a connection to the card is automatically +created and we can send APDU commands to the card using this connection. + +Requesting a card by ATR +------------------------ + +The following scripts requests a card with a known ATR:: + + >>> from smartcard.CardType import ATRCardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.util import toHexString, toBytes + >>> + >>> cardtype = ATRCardType( toBytes( "3B 16 94 20 02 01 00 00 0D" ) ) + >>> cardrequest = CardRequest( timeout=1, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> cardservice.connection.connect() + >>> print toHexString( cardservice.connection.getATR() ) + 3B 16 94 20 02 01 00 00 0D + >>> + >>> SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + >>> DF_TELECOM = [0x7F, 0x10] + >>> data, sw1, sw2 = cardservice.connection.transmit( SELECT + DF_TELECOM ) + >>> print "%x %x" % (sw1, sw2) + 9f 1a + >>> + +To request a card with a know ATR, you must first create an `ATRCardType +<http://pyscard.sourceforge.net/epydoc/smartcard.CardType.ATRCardType-class.html>`_ +object with the desired ATR:: + + >>> cardtype = ATRCardType( toBytes( "3B 16 94 20 02 01 00 00 0D" ) ) + +And then create a `CardRequest +<http://pyscard.sourceforge.net/epydoc/smartcard.CardRequest.CardRequest-class.html>`_ +for this card type. In the sample, we request a time-out of 1 second. + + >>> cardrequest = CardRequest( timeout=1, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + +The waitforcard() will either return with a card service or a time-out. +The card service connection attribute can be used thereafter to transmit +APDU commands to the card, as with the reader centric approach. + + >>> cardservice.connection.connect() + >>> print toHexString( cardservice.connection.getATR() ) + +If necessary, the reader used for the connection can be accessed thru +the `CardConnection +<http://pyscard.sourceforge.net/epydoc/smartcard.CardConnection.CardConnection-class.html>`_ +object: + + >>> print cardservice.connection.getReader() + SchlumbergerSema Reflex USB v.2 0 + +The `ATRCardType +<http://pyscard.sourceforge.net/epydoc/smartcard.CardType.ATRCardType-class.html>`_ +also supports masks: + + >>> from smartcard.CardType import ATRCardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.util import toHexString, toBytes + >>> + >>> cardtype = ATRCardType( toBytes( "3B 15 94 20 02 01 00 00 0F" ), toBytes( "00 00 FF FF FF FF FF FF 00" ) ) + >>> cardrequest = CardRequest( timeout=1, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> cardservice.connection.connect() + >>> print toHexString( cardservice.connection.getATR() ) + 3B 16 94 20 02 01 00 00 0D + +Other CardTypes are available, and new CardTypes can be created, as +described below. + +Requesting any card +------------------- + +The `AnyCardType +<http://pyscard.sourceforge.net/epydoc/smartcard.CardType.AnyCardType-class.html>`_ +is useful for requesting any card in any reader: + + >>> from smartcard.CardType import AnyCardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.util import toHexString + >>> + >>> cardtype = AnyCardType() + >>> cardrequest = CardRequest( timeout=1, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> cardservice.connection.connect() + >>> print toHexString( cardservice.connection.getATR() ) + 3B 16 94 20 02 01 00 00 0D + >>> print cardservice.connection.getReader() + SchlumbergerSema Reflex USB v.2 0 + +Custom CardTypes +---------------- + +Custom CardTypes can be created, e.g. a card type that checks the ATR +and the historical bytes of the card. To create a custom CardType, +deriver your CardType class from the `CardType +<http://pyscard.sourceforge.net/epydoc/smartcard.CardType.CardType-class.html>`_ +base class (or any other CardType) and override the matches() method. +For exemple to create a DCCardType that will match cards with the direct +convention (first byte of ATR to 0x3b): + + >>> from smartcard.CardType import CardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.util import toHexString + >>> + >>> class DCCardType(CardType): + ... def matches( self, atr, reader=None ): + ... return atr[0]==0x3B + ... + >>> cardtype = DCCardType() + >>> cardrequest = CardRequest( timeout=1, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> cardservice.connection.connect() + >>> print toHexString( cardservice.connection.getATR() ) + 3B 16 94 20 02 01 00 00 0D + >>> print cardservice.connection.getReader() + SchlumbergerSema Reflex USB v.2 0 + >>> + +Scripts written with the card-centric approach fixes the problems of the +reader-centric approach: + +* there is no assumption concerning the reader index or reader name; the + desired card will be located in any reader + +* the request will block or time-out if the desired card type is not + inserted since we request the desired card type, the script is not + played on an unknown or uncompatible card + +Scripts written with the card-centric approach have however the +following drawbacks: + +* the script is limited to a specific card type; we have to modify the + script if we want to execute the script on another card type. For + exemple, we have to modify the ATR of the card if we are using the + ATRCardType. This can be partially solved by having a custom CardType + that matches several ATRs, though. + +Selecting the card communication protocol +----------------------------------------- + +Communication parameters are mostly important for the protocol +negociation between the smart card reader and the card. The main +smartcard protocols are the T=0 protocol and the T=1 protocol, for byte +or block transmission, respectively. The required protocol can be +specified at card connection or card transmission. + +By defaults, the connect() method of the CardConnection object.will try +to connect using either the T=0 or T=1 protocol. To force a connection +protocol, you can pass the required protocol to the connect() method. + + >>> from smartcard.CardType import AnyCardType + >>> from smartcard.CardConnection import CardConnection + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.util import toHexString + >>> + >>> cardtype = AnyCardType() + >>> cardrequest = CardRequest( timeout=1, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> cardservice.connection.connect( CardConnection.T1_protocol ) + >>> print toHexString( cardservice.connection.getATR() ) + 3B 16 94 20 02 01 00 00 0D + >>> print cardservice.connection.getReader() + SchlumbergerSema Reflex USB v.2 0 + +Alternatively, you can specify the required protocol in the +CardConnection transmit() method: + + >>> from smartcard.CardType import AnyCardType + >>> from smartcard.CardConnection import CardConnection + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.util import toHexString, toBytes + >>> + >>> cardtype = AnyCardType() + >>> cardrequest = CardRequest( timeout=1, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> cardservice.connection.connect() + >>> + >>> SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + >>> DF_TELECOM = [0x7F, 0x10] + >>> + >>> apdu = SELECT+DF_TELECOM + >>> print 'sending ' + toHexString(apdu) + sending A0 A4 00 00 02 7F 10 + >>> response, sw1, sw2 = cardservice.connection.transmit( apdu, CardConnection.T1_protocol ) + >>> print 'response: ', response, ' status words: ', "%x %x" % (sw1, sw2) + response: [] status words: 9f 1a + >>> + >>> if sw1 == 0x9F: + ... GET_RESPONSE = [0XA0, 0XC0, 00, 00 ] + ... apdu = GET_RESPONSE + [sw2] + ... print 'sending ' + toHexString(apdu) + ... response, sw1, sw2 = cardservice.connection.transmit( apdu ) + ... print 'response: ', toHexString(response), ' status words: ', "%x %x" % (sw1, sw2) + ... + sending A0 C0 00 00 1A + response: 00 00 00 00 7F 10 02 00 00 00 00 00 0D 13 00 0A 04 00 83 8A 83 8A 00 01 00 00 status words: 90 0 + >>> + +The object-centric approach +=========================== + +In the object-centric approach, we associate a high-level object with a +set of smart cards supported by the object. For example we associate a +javacard loader class with a set of javacard smart cards. We create a +request for the specific object, and wait until a card supported by the +object is inserted. Once a card supported by the object is inserted, we +perform the required function by calling the objec methods. + +To be written... + +Tracing APDUs +************* + +The brute force +=============== + +A straightforward way of tracing command and response APDUs is to insert +print statements around the transmit() method calls: + + >>> from smartcard.CardType import ATRCardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.util import toHexString, toBytes + >>> + >>> cardtype = ATRCardType( toBytes( "3B 16 94 20 02 01 00 00 0D" ) ) + >>> cardrequest = CardRequest( timeout=1, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> cardservice.connection.connect() + >>> + >>> SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + >>> DF_TELECOM = [0x7F, 0x10] + >>> + >>> apdu = SELECT+DF_TELECOM + >>> print 'sending ' + toHexString(apdu) + sending A0 A4 00 00 02 7F 10 + >>> response, sw1, sw2 = cardservice.connection.transmit( apdu ) + >>> print 'response: ', response, ' status words: ', "%x %x" % (sw1, sw2) + response: [] status words: 9f 1a + >>> + >>> if sw1 == 0x9F: + ... GET_RESPONSE = [0XA0, 0XC0, 00, 00 ] + ... apdu = GET_RESPONSE + [sw2] + ... print 'sending ' + toHexString(apdu) + ... response, sw1, sw2 = cardservice.connection.transmit( apdu ) + ... print 'response: ', toHexString(response), ' status words: ', "%x %x" % (sw1, sw2) + ... + sending A0 C0 00 00 1A + response: 00 00 00 00 7F 10 02 00 00 00 00 00 0D 13 00 0A 04 00 83 8A 83 8A 00 01 00 00 status words: 90 0 + >>> + +Scripts written this way are quite difficult to read, because there are +more tracing statements than actual apdu transmits.. + +A small improvement in visibility would be to replace the print +instructions by functions, e.g.: + + >>> from smartcard.CardType import ATRCardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.util import toHexString, toBytes + >>> + >>> cardtype = ATRCardType( toBytes( "3B 16 94 20 02 01 00 00 0D" ) ) + >>> cardrequest = CardRequest( timeout=1, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> cardservice.connection.connect() + >>> + >>> SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + >>> DF_TELECOM = [0x7F, 0x10] + >>> + >>> def trace_command(apdu): + ... print 'sending ' + toHexString(apdu) + ... + >>> def trace_response( response, sw1, sw2 ): + ... if None==response: response=[] + ... print 'response: ', toHexString(response), ' status words: ', "%x %x" % (sw1, sw2) + ... + >>> apdu = SELECT+DF_TELECOM + >>> trace_command(apdu) + sending A0 A4 00 00 02 7F 10 + >>> response, sw1, sw2 = cardservice.connection.transmit( apdu ) + >>> trace_response( response, sw1, sw2 ) + response: status words: 9f 1a + >>> + >>> if sw1 == 0x9F: + ... GET_RESPONSE = [0XA0, 0XC0, 00, 00 ] + ... apdu = GET_RESPONSE + [sw2] + ... trace_command(apdu) + ... response, sw1, sw2 = cardservice.connection.transmit( apdu ) + ... trace_response( response, sw1, sw2 ) + ... + sending A0 C0 00 00 1A + response: 00 00 00 00 7F 10 02 00 00 00 00 00 0D 13 00 0A 04 00 83 8A 83 8A 00 01 00 00 status words: 90 0 + >>> + +Using card connection observers to trace apdu transmission +========================================================== + +The prefered solution is to implement a card connection observer, and +register the observer with the card connection. The card connection will +then notify the observer when card connection events occur (e.g. +connection, disconnection, apdu command or apdu response). This is +illustrated in the following script: + + >>> from smartcard.CardType import AnyCardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver + >>> + >>> GET_RESPONSE = [0XA0, 0XC0, 00, 00 ] + >>> SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + >>> DF_TELECOM = [0x7F, 0x10] + >>> + >>> + >>> cardtype = AnyCardType() + >>> cardrequest = CardRequest( timeout=10, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> observer=ConsoleCardConnectionObserver() + >>> cardservice.connection.addObserver( observer ) + >>> + >>> cardservice.connection.connect() + connecting to SchlumbergerSema Reflex USB v.2 0 + >>> + >>> apdu = SELECT+DF_TELECOM + >>> response, sw1, sw2 = cardservice.connection.transmit( apdu ) + > A0 A4 00 00 02 7F 10 + < [] 9F 1A + >>> if sw1 == 0x9F: + ... apdu = GET_RESPONSE + [sw2] + ... response, sw1, sw2 = cardservice.connection.transmit( apdu ) + ... else: + ... print 'no DF_TELECOM' + ... + > A0 C0 00 00 1A + < 00 00 00 00 7F 10 02 00 00 00 00 00 0D 13 00 0A 04 00 83 8A 83 8A 00 01 00 00 90 0 + >>> + +In this script, a `ConsoleCardConnectionObserver +<http://pyscard.sourceforge.net/epydoc/smartcard.CardConnectionObserver.ConsoleCardConnectionObserver-class.html>`_ +is attached to the card service connection once the watiforcard() call +returns. + + >>> observer=ConsoleCardConnectionObserver() + >>> cardservice.connection.addObserver( observer ) + +On card connection events (connect, disconnect, transmit command apdu, +receive response apdu), the card connection notifies its obersers with a +`CarConnectionEvent +<http://pyscard.sourceforge.net/epydoc/smartcard.CardConnectionEvent.CardConnectionEvent-class.html>`_ +including the event type and the event data. The +`ConsoleCardConnectionObserver +<http://pyscard.sourceforge.net/epydoc/smartcard.CardConnectionObserver.ConsoleCardConnectionObserver-class.html>`_ +is a simple observer that will print on the console the card connection +events. The class definition is the following: + +.. sourcecode:: python + + class ConsoleCardConnectionObserver( CardConnectionObserver ): + def update( self, cardconnection, ccevent ): + + if 'connect'==ccevent.type: + print 'connecting to ' + cardconnection.getReader() + + elif 'disconnect'==ccevent.type: + print 'disconnecting from ' + cardconnection.getReader() + + elif 'command'==ccevent.type: + print '> ', toHexString( ccevent.args[0] ) + + elif 'response'==ccevent.type: + if []==ccevent.args[0]: + print '< [] ', "%-2X %-2X" % tuple(ccevent.args[-2:]) + else: + print '< ', toHexString(ccevent.args[0]), "%-2X %-2X" % tuple(ccevent.args[-2:]) + +The console card connection observer is thus printing the connect, +disconnect, command and response apdu events: + + >>> cardservice.connection.connect() + connecting to SchlumbergerSema Reflex USB v.2 0 + >>> + >>> apdu = SELECT+DF_TELECOM + >>> response, sw1, sw2 = cardservice.connection.transmit( apdu ) + > A0 A4 00 00 02 7F 10 + < [] 9F 1A + >>> if sw1 == 0x9F: + ... apdu = GET_RESPONSE + [sw2] + ... response, sw1, sw2 = cardservice.connection.transmit( apdu ) + ... else: + ... print 'no DF_TELECOM' + ... + > A0 C0 00 00 1A + < 00 00 00 00 7F 10 02 00 00 00 00 00 0D 13 00 0A 04 00 83 8A 83 8A 00 01 00 00 90 0 + +A card connection observer's update methode is called upon card +connection event, with the connection and the connection event as +parameters. The `CardConnectionEvent +<http://pyscard.sourceforge.net/epydoc/smartcard.CardConnectionEvent.CardConnectionEvent-class.html>`_ +class definition is the following: + +.. sourcecode:: python + + class CardConnectionEvent: + """Base class for card connection events. + + This event is notified by CardConnection objects. + + type: 'connect', 'disconnect', 'command', 'response' + args: None for 'connect' or 'disconnect' + command APDU byte list for 'command' + [response data, sw1, sw2] for 'response' + type: 'connect' args:""" + def __init__( self, type, args=None): + self.type=type + self.args=args + +You can write your own card connection observer, for example to perform +fancy output in a wxWindows frame, or apdu interpretation. The following +scripts defines a small SELECT and GET RESPONSE apdu interpreter: + + >>> from smartcard.CardType import AnyCardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.CardConnectionObserver import CardConnectionObserver + >>> from smartcard.util import toHexString + >>> + >>> from string import replace + >>> + >>> class TracerAndSELECTInterpreter( CardConnectionObserver ): + ... def update( self, cardconnection, ccevent ): + ... if 'connect'==ccevent.type: + ... print 'connecting to ' + cardconnection.getReader() + ... elif 'disconnect'==ccevent.type: + ... print 'disconnecting from ' + cardconnection.getReader() + ... elif 'command'==ccevent.type: + ... str=toHexString(ccevent.args[0]) + ... str = replace( str , "A0 A4 00 00 02", "SELECT" ) + ... str = replace( str , "A0 C0 00 00", "GET RESPONSE" ) + ... print '> ', str + ... elif 'response'==ccevent.type: + ... if []==ccevent.args[0]: + ... print '< [] ', "%-2X %-2X" % tuple(ccevent.args[-2:]) + ... else: + ... print '< ', toHexString(ccevent.args[0]), "%-2X %-2X" % tuple(ccevent.args[-2:]) + ... + >>> + >>> GET_RESPONSE = [0XA0, 0XC0, 00, 00 ] + >>> SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + >>> DF_TELECOM = [0x7F, 0x10] + >>> + >>> + >>> cardtype = AnyCardType() + >>> cardrequest = CardRequest( timeout=10, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> observer=TracerAndSELECTInterpreter() + >>> cardservice.connection.addObserver( observer ) + >>> + >>> cardservice.connection.connect() + connecting to SchlumbergerSema Reflex USB v.2 0 + >>> + >>> apdu = SELECT+DF_TELECOM + >>> response, sw1, sw2 = cardservice.connection.transmit( apdu ) + > SELECT 7F 10 + < [] 9F 1A + >>> if sw1 == 0x9F: + ... apdu = GET_RESPONSE + [sw2] + ... response, sw1, sw2 = cardservice.connection.transmit( apdu ) + ... else: + ... print 'no DF_TELECOM' + ... + > GET RESPONSE 1A + < 00 00 00 00 7F 10 02 00 00 00 00 00 0D 13 00 0A 04 00 83 8A 83 8A 00 01 00 00 90 0 + >>> + +Testing for APDU transmission errors +************************************ + +Upon transmission and processing of an APDU, the smart card returns a +pair of status words, SW1 and SW2, to report various success or error +codes following the required processing. Some of these success or error +codes are standardized in ISO7816-4, ISO7816-8 or ISO7816-9, for +example. Other status word codes are standardized by standardization +bodies such as Open Platform (e.g. javacard), 3GPP (e.g. SIM or USIM +cards), or Eurocard-Mastercard-Visa (EMV) (e.g. banking cards). Finally, +any smart card application developper can defined application related +proprietary codes; for example the MUSCLE applet defines a set of +prioprietary codes related to the MUSCLE applet features. + +Some of these status word codes are uniques, but others have a different +meaning depending on the card type and its supported standards. For +example, ISO7816-4 defines the error code 0x62 0x82 as "File +Invalidated", whereas in Open Platform 2.1 the same error code is +defined as "Card life cycle is CARD_LOCKED". As a result, the list of +error codes that can be returned by a smart card and they interpretation +depend on the card type. The following discussion outlines possible +strategies to check and report smart card status word errors. + +The brute force for testing APDU transmission errors +==================================================== + +As for APDU tracing, a straightforward way of checking for errors in response APDUs during the execution of scripts is to insert testt statements after the transmit() method calls: + + >>> from smartcard.CardType import AnyCardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver + >>> + >>> GET_RESPONSE = [0XA0, 0XC0, 00, 00 ] + >>> SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + >>> DF_TELECOM = [0x7F, 0x10] + >>> + >>> cardtype = AnyCardType() + >>> cardrequest = CardRequest( timeout=10, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> observer=ConsoleCardConnectionObserver() + >>> cardservice.connection.addObserver( observer ) + >>> + >>> cardservice.connection.connect() + connecting to Utimaco CardManUSB 0 + >>> + >>> apdu = SELECT+DF_TELECOM + >>> response, sw1, sw2 = cardservice.connection.transmit( apdu ) + > A0 A4 00 00 02 7F 10 + < [] 6E 0 + >>> + >>> if sw1 in range(0x61, 0x6f): + ... print "Error: sw1: %x sw2: %x" % (sw1, sw2) + ... + Error: sw1: 6e sw2: 0 + >>> if sw1 == 0x9F: + ... apdu = GET_RESPONSE + [sw2] + ... response, sw1, sw2 = cardservice.connection.transmit( apdu ) + ... + >>> cardservice.connection.disconnect() + disconnecting from Utimaco CardManUSB 0 + >>> + +Scripts written this way are quite difficult to read, because there are +more error detection statements than actual apdu transmits. + +An improvement in visibility is to wrap the transmit instruction inside +a function mytransmit, e.g.: + + >>> from smartcard.CardType import AnyCardType + >>> from smartcard.CardRequest import CardRequest + >>> from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver + >>> + >>> def mytransmit( connection, apdu ): + ... response, sw1, sw2 = connection.transmit( apdu ) + ... if sw1 in range(0x61, 0x6f): + ... print "Error: sw1: %x sw2: %x" % (sw1, sw2) + ... return response, sw1, sw2 + ... + >>> + >>> GET_RESPONSE = [0XA0, 0XC0, 00, 00 ] + >>> SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + >>> DF_TELECOM = [0x7F, 0x10] + >>> + >>> + >>> cardtype = AnyCardType() + >>> cardrequest = CardRequest( timeout=10, cardType=cardtype ) + >>> cardservice = cardrequest.waitforcard() + >>> + >>> observer=ConsoleCardConnectionObserver() + >>> cardservice.connection.addObserver( observer ) + >>> + >>> cardservice.connection.connect() + connecting to Utimaco CardManUSB 0 + >>> + >>> apdu = SELECT+DF_TELECOM + >>> response, sw1, sw2 = mytransmit( cardservice.connection, apdu ) + > A0 A4 00 00 02 7F 10 + < [] 6E 0 + Error: sw1: 6e sw2: 0 + >>> + >>> if sw1 == 0x9F: + ... apdu = GET_RESPONSE + [sw2] + ... response, sw1, sw2 = mytransmit( cardservice.connection, apdu ) + ... + >>> cardservice.connection.disconnect() + disconnecting from Utimaco CardManUSB 0 + >>> + +The prefered solution is for testing errors is to use +smarcard.sw.ErrorChecker, as described in the following section. + +Checking APDU transmission errors with error checkers +===================================================== + +Status word errors can occur from different sources. The ISO7816-4 +standards defines status words for sw1 in the range 0x62 to 0x6F and +some values of sw2, except for 0x66 which is reserved for security +related issues. The ISO7816-8 standards define other status words, e.g. +sw1=0x68 and sw2=0x83 or 0x84 for command chaining errors. Other +standards, like Open Platform, define additional status words error, +e.g. sw1=0x94 and sw2=0x84. + +The prefered strategy for status word error checking is based around +individual error checkers (smartcard.sw.ErrorChecker) that can be +chained into an error checking chain (smartcars.sw.ErrorCheckingChain). + +Error checkers +-------------- + +An error checker is a class deriving from `ErrorChecker +<http://pyscard.sourceforge.net/epydoc/smartcard.sw.ErrorChecker.ErrorChecker-class.html>`_ +that checks for recognized sw1, sw2 error conditions when called, and +raises an exception when finding such condition. This is illustrated in +the following sample: + + >>> from smartcard.sw.ISO7816_4ErrorChecker import ISO7816_4ErrorChecker + >>> + >>> errorchecker=ISO7816_4ErrorChecker() + >>> errorchecker( [], 0x90, 0x00 ) + >>> errorchecker( [], 0x6A, 0x80 ) + Traceback (most recent call last): + File "<stdin>", line 1, in ? + File "D:\projects\pyscard-install\factory\python\lib\site-packages\smartcard\sw\ISO7816_4ErrorChecker.py", line 137, in __call__ + raise exception( data, sw1, sw2, message ) + smartcard.sw.SWExceptions.CheckingErrorException: 'Status word exception: checking error - Incorrect parameters in the data field!' + >>> + +The first call to error checker does not raise an exception, since 90 00 +does not report any error. The second calls however raises a +CheckingErrorException. + +Error checking chains +--------------------- + +Error checkers can be chained into `error checking chain +<http://pyscard.sourceforge.net/epydoc/smartcard.sw.ErrorCheckingChain.ErrorCheckingChain-class.html>`_. +Each checker in the chain is called until an error condition is met, in +which case an exception is raised. This is illustrated in the following +sample: + + >>> from smartcard.sw.ISO7816_4ErrorChecker import ISO7816_4ErrorChecker + >>> from smartcard.sw.ISO7816_8ErrorChecker import ISO7816_8ErrorChecker + >>> from smartcard.sw.ISO7816_9ErrorChecker import ISO7816_9ErrorChecker + >>> + >>> from smartcard.sw.ErrorCheckingChain import ErrorCheckingChain + >>> + >>> errorchain = [] + >>> errorchain=[ ErrorCheckingChain( errorchain, ISO7816_9ErrorChecker() ), + ... ErrorCheckingChain( errorchain, ISO7816_8ErrorChecker() ), + ... ErrorCheckingChain( errorchain, ISO7816_4ErrorChecker() ) ] + >>> + >>> errorchain[0]( [], 0x90, 0x00 ) + >>> errorchain[0]( [], 0x6A, 0x8a ) + Traceback (most recent call last): + File "<stdin>", line 1, in ? + File "D:\projects\pyscard-install\factory\python\lib\site-packages\smartcard\sw\ErrorCheckingChain.py", line 60, + in __call__ + self.strategy( data, sw1, sw2 ) + File "D:\projects\pyscard-install\factory\python\lib\site-packages\smartcard\sw\ISO7816_9ErrorChecker.py", line 74, in __call__ + raise exception( data, sw1, sw2, message ) + smartcard.sw.SWExceptions.CheckingErrorException: 'Status word exception: checking error - DF name already exists!' + >>> + +In this sample, an error checking chain is created that will check first +for iso 7816-9 errors, then iso7816-8 errors, and finally iso7816-4 +errors. + +The first call to the error chain does not raise an exception, since 90 +00 does not report any error. The second calls however raises a +CheckingErrorException, caused by the iso7816-9 error checker. + +Filtering exceptions +-------------------- + +You can filter undesired exceptions in a chain by adding a filtered +exception to the error checking chain:: + + >>> from smartcard.sw.ISO7816_4ErrorChecker import ISO7816_4ErrorChecker + >>> from smartcard.sw.ISO7816_8ErrorChecker import ISO7816_8ErrorChecker + >>> from smartcard.sw.ISO7816_9ErrorChecker import ISO7816_9ErrorChecker + >>> + >>> from smartcard.sw.ErrorCheckingChain import ErrorCheckingChain + >>> + >>> errorchain = [] + >>> errorchain=[ ErrorCheckingChain( errorchain, ISO7816_9ErrorChecker() ), + ... ErrorCheckingChain( errorchain, ISO7816_8ErrorChecker() ), + ... ErrorCheckingChain( errorchain, ISO7816_4ErrorChecker() ) ] + >>> + >>> + >>> errorchain[0]( [], 0x90, 0x00 ) + >>> errorchain[0]( ... [truncated message content] |