Initial commit.

git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@1 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
schertz123456
2009-08-06 20:25:45 +00:00
commit 93f814548c
28 changed files with 9667 additions and 0 deletions

6
Makefile Normal file
View File

@@ -0,0 +1,6 @@
SUBDIRS = ccid virtualsmartcard
default: all
%:
for d in $(SUBDIRS); do $(MAKE) $@ -C $$d; done

1
ccid/AUTHORS Normal file
View File

@@ -0,0 +1 @@
Frank Morgner <morgner@informatik.hu-berlin.de>

674
ccid/COPYING Normal file
View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

14
ccid/INSTALL Normal file
View File

@@ -0,0 +1,14 @@
Installation is simple; type `make install`. If you want to customize the
compilation or install process, you will need to pass variables to make.
DESTDIR prefix for installation
LIBPCSCLITE_CFLAGS compiler flags for pcsclite usage (needed if no
pkg-config is available)
See the Makefile for more details.
Example:
make DESTDIR=/tmp/stage install
Note: If you want to uninstall the binary, you need to pass the same DESTDIR
to make, for example `make DESTDIR=/tmp/stage uninstall`.

46
ccid/Makefile Normal file
View File

@@ -0,0 +1,46 @@
MAKEFLAGS += -rR --no-print-directory
# Directories
prefix =
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
# Compiler
CC = gcc
CFLAGS = -Wall -g
LIBPCSCLITE_CFLAGS = `pkg-config --cflags --libs libpcsclite`
PTHREAD_CFLAGS = -pthread
INSTALL = install
INSTALL_PROGRAM = $(INSTALL)
TARGETS = ccid
# top-level rule
all: $(TARGETS)
ccid: ccid.h ccid.c usbstring.c usbstring.h
$(CC) $(LIBPCSCLITE_CFLAGS) $(PTHREAD_CFLAGS) $(CFLAGS) usbstring.c ccid.c -o $@
install: $(TARGETS) installdirs
$(INSTALL_PROGRAM) ccid $(DESTDIR)$(bindir)
.PHONY: installdirs
installdirs:
$(INSTALL) -d $(DESTDIR)$(bindir)
.PHONY: install-strip
install-strip:
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
.PHONY: uninstall
uninstall:
rm -f $(DESTDIR)$(bindir)/ccid
.PHONY: clean
clean:
rm -f ccid

26
ccid/README Normal file
View File

@@ -0,0 +1,26 @@
Title : ccid
Authors : Frank Morgner
Version : 1.0.0 27 Jul 2009
License : See file COPYING
Tested Platforms : Linux 2.6 (Debian, Ubuntu, Om 2008)
Welcome to ccid. The purpose of ccid is to forward a PCSC smartcard reader
as a standard USB CCID reader. If the host system is in USB device mode, ccid
forwards the local reader via USB to an other device. If in USB host mode,
ccid virtually plugges in a USB CCID reader to the host system.
ccid is implemented using GadgetFS. The source code is based on the GadgetFS
example at http://www.linux-usb.org/gadget/.
INSTALLATION
------------
See file INSTALL.
QUESTIONS
---------
For questions, please email me at:
Frank Morgner <morgner@informatik.hu-berlin.de>

32
ccid/ccid.bb Normal file
View File

@@ -0,0 +1,32 @@
DESCRIPTION = "Virtual Smartcard with PCSC Driver and CCID to PCSC Gadget"
LICENSE = "GPL"
DEPENDS = "pcsc-lite linux-libc-headers"
RDEPENDS = "pcsc-lite kernel-module-gadgetfs"
SRC_URI = "svn://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard;module=ccid;proto=https;rev=1"
S = "${WORKDIR}"
LIBPCSCLITE_CFLAGS = -I${STAGING_INCDIR}/PCSC -lpcsclite
PTHREAD_CFLAGS = -pthread
# FIXME: let openmoko set the right STAGING_KERNEL_DIR
STAGING_KERNEL_DIR = ${STAGING_DIR}/${MACHINE}-angstrom-${TARGET_OS}/kernel
GADGETFS_CFLAGS = -I${STAGING_KERNEL_DIR}/include
INSTALL = install
INSTALL_PROGRAM = ${INSTALL}
FILES_${PN} = "\
${bindir}/* \
"
do_compile() {
${CC} ${S}/ccid/ccid.c ${S}/ccid/usbstring.c -o ${S}/ccid/ccid \
${LIBPCSCLITE_CFLAGS} ${GADGETFS_CFLAGS} ${PTHREAD_CFLAGS} ${CFLAGS}
}
do_install() {
${INSTALL} -d ${D}${bindir}
${INSTALL_PROGRAM} ${S}/ccid/ccid ${D}${bindir}
}

1790
ccid/ccid.c Normal file

File diff suppressed because it is too large Load Diff

854
ccid/ccid.h Normal file
View File

@@ -0,0 +1,854 @@
/*
* Copyright (C) 2009 Frank Morgner
*
* This file is part of ccid.
*
* ccid is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* ccid 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* ccid. If not, see <http://www.gnu.org/licenses/>.
*/
#include <winscard.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#ifndef USB_REQ_CCID
#define USB_REQ_CCID 0xA1
#define CCID_CONTROL_ABORT 0x01
#define CCID_CONTROL_GET_CLOCK_FREQUENCIES 0x02
#define CCID_CONTROL_GET_DATA_RATES 0x03
struct ccid_class_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdCCID;
__u8 bMaxSlotIndex;
__u8 bVoltageSupport;
__le32 dwProtocols;
__le32 dwDefaultClock;
__le32 dwMaximumClock;
__u8 bNumClockSupport;
__le32 dwDataRate;
__le32 dwMaxDataRate;
__u8 bNumDataRatesSupported;
__le32 dwMaxIFSD;
__le32 dwSynchProtocols;
__le32 dwMechanical;
__le32 dwFeatures;
__le32 dwMaxCCIDMessageLength;
__u8 bClassGetResponse;
__u8 bclassEnvelope;
__le16 wLcdLayout;
__u8 bPINSupport;
__u8 bMaxCCIDBusySlots;
} __attribute__ ((packed));
typedef struct {
__u8 bmFindexDindex;
__u8 bmTCCKST0;
__u8 bGuardTimeT0;
__u8 bWaitingIntegerT0;
__u8 bClockStop;
} __attribute__ ((packed)) abProtocolDataStructure_T0_t;
typedef struct {
__u8 bmFindexDindex;
__u8 bmTCCKST1;
__u8 bGuardTimeT1;
__u8 bWaitingIntegersT1;
__u8 bClockStop;
__u8 bIFSC;
__u8 bNadValue;
} __attribute__ ((packed)) abProtocolDataStructure_T1_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bBWI;
__le16 wLevelParameter;
} __attribute__ ((packed)) PC_to_RDR_XfrBlock_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_IccPowerOff_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_GetSlotStatus_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_GetParameters_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_ResetParameters_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bProtocolNum;
__le16 abRFU;
} __attribute__ ((packed)) PC_to_RDR_SetParameters_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bPowerSelect;
__le16 abRFU;
} __attribute__ ((packed)) PC_to_RDR_IccPowerOn_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bClockStatus;
} __attribute__ ((packed)) RDR_to_PC_SlotStatus_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bChainParameter;
} __attribute__ ((packed)) RDR_to_PC_DataBlock_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bProtocolNum;
} __attribute__ ((packed)) RDR_to_PC_Parameters_t;
typedef struct {
__u8 bMessageType;
__u8 bmSlotICCState;
} __attribute__ ((packed)) RDR_to_PC_NotifySlotChange_t;
#endif
struct hid_class_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdHID;
__le32 bCountryCode;
__u8 bNumDescriptors;
} __attribute__ ((packed));
static struct ccid_class_descriptor
ccid_desc = {
.bLength = sizeof ccid_desc,
.bDescriptorType = 0x21,
.bcdCCID = __constant_cpu_to_le16(0x0110),
.bMaxSlotIndex = 0,
.bVoltageSupport = 0x01,
.dwProtocols = __constant_cpu_to_le32(0x01| // T=0
0x02), // T=1
.dwDefaultClock = __constant_cpu_to_le32(0xDFC),
.dwMaximumClock = __constant_cpu_to_le32(0xDFC),
.bNumClockSupport = 1,
.dwDataRate = __constant_cpu_to_le32(0x2580),
.dwMaxDataRate = __constant_cpu_to_le32(0x2580),
.bNumDataRatesSupported = 1,
.dwMaxIFSD = __constant_cpu_to_le32(0xFF), // FIXME
.dwSynchProtocols = __constant_cpu_to_le32(0),
.dwMechanical = __constant_cpu_to_le32(0),
.dwFeatures = __constant_cpu_to_le32(0x2|
// Automatic parameter configuration based on ATR data
0x8| // Automatic ICC voltage selection
0x10| // Automatic ICC clock frequency change
0x20| // Automatic baud rate change
0x40| // Automatic parameters negotiation
0x80| // Automatic PPS
0x20000| // Short APDU level exchange
0x100000),// USB Wake up signaling supported
.dwMaxCCIDMessageLength = __constant_cpu_to_le32(261+10),
.bClassGetResponse = 0xFF,
.bclassEnvelope = 0xFF,
.wLcdLayout = __constant_cpu_to_le16(0),
.bPINSupport = 0,
.bMaxCCIDBusySlots = 1,
};
SCARDCONTEXT hcontext = 0;
SCARDHANDLE hcard = 0;
SCARD_READERSTATE rstate;
DWORD dwActiveProtocol;
char reader_name[MAX_READERNAME];
int reader_num;
char* perform_initialization(int num)
{
char *readers, *str;
DWORD size;
LONG r;
reader_num = num;
r = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hcontext);
if (r != SCARD_S_SUCCESS) {
fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
SCardReleaseContext(hcontext);
return NULL;
}
r = SCardListReaders(hcontext, NULL, NULL, &size);
if (size == 0)
r = SCARD_E_UNKNOWN_READER;
if (r != SCARD_S_SUCCESS) {
fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
SCardReleaseContext(hcontext);
return NULL;
}
/* get all readers */
readers = (char *) malloc(size);
if (readers == NULL) {
fprintf(stderr, "pc/sc error: %s\n",
pcsc_stringify_error(SCARD_E_NO_MEMORY));
SCardReleaseContext(hcontext);
return NULL;
}
r = SCardListReaders(hcontext, NULL, readers, &size);
if (r != SCARD_S_SUCCESS) {
free(readers);
fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
SCardReleaseContext(hcontext);
return NULL;
}
/* name of reader number num */
str = readers;
for (size = 0; size < num; size++) {
/* go to the next name */
str += strlen(str) + 1;
/* no more readers available? */
if (strlen(str) == 0) {
free(readers);
fprintf(stderr, "pc/sc error: %s\n",
pcsc_stringify_error(SCARD_E_UNKNOWN_READER));
SCardReleaseContext(hcontext);
return NULL;
}
}
strncpy(reader_name, str, MAX_READERNAME);
free(readers);
rstate.dwCurrentState = SCARD_STATE_UNAWARE;
rstate.dwEventState = SCARD_STATE_UNAWARE;
rstate.szReader = reader_name;
return reader_name;
}
int perform_shutdown()
{
SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
hcard = 0;
rstate.dwCurrentState = SCARD_STATE_UNAWARE;
rstate.dwEventState = SCARD_STATE_UNAWARE;
return SCardReleaseContext(hcontext);
}
__u8 get_bError(LONG pcsc_result)
{
switch (pcsc_result) {
case SCARD_S_SUCCESS : /**< No error was encountered. */
// Command not supported
return 0;
case SCARD_E_CANCELLED : /**< The action was cancelled by an SCardCancel request. */
fprintf(stderr, "CMD_ABORTED\n");
return 0xFF;
case SCARD_E_INVALID_HANDLE : /**< The supplied handle was invalid. */
case SCARD_E_NO_SMARTCARD : /**< The operation requires a Smart Card, but no Smart Card is currently in the device. */
case SCARD_E_UNKNOWN_CARD : /**< The specified smart card name is not recognized. */
case SCARD_E_NOT_READY : /**< The reader or smart card is not ready to accept commands. */
case SCARD_W_UNRESPONSIVE_CARD : /**< The smart card is not responding to a reset. */
case SCARD_W_UNPOWERED_CARD : /**< Power has been removed from the smart card, so that further communication is not possible. */
case SCARD_W_REMOVED_CARD : /**< The smart card has been removed, so further communication is not possible. */
fprintf(stderr, "ICC_MUTE\n");
return 0xFE;
case SCARD_E_SHARING_VIOLATION : /**< The smart card cannot be accessed because of other connections outstanding. */
fprintf(stderr, "CMD_SLOT_BUSY\n");
return 0xE0;
case SCARD_E_PROTO_MISMATCH : /**< The requested protocols are incompatible with the protocol currently in use with the smart card. */
fprintf(stderr, "ICC_PROTOCOL_NOT_SUPPORTED\n");
return 0xF6;
// case SCARD_F_INTERNAL_ERROR : /**< An internal consistency check failed. */
// case SCARD_E_INVALID_PARAMETER : /**< One or more of the supplied parameters could not be properly interpreted. */
// case SCARD_E_INVALID_TARGET : /**< Registry startup information is missing or invalid. */
// case SCARD_E_NO_MEMORY : /**< Not enough memory available to complete this command. */
// case SCARD_F_WAITED_TOO_LONG : /**< An internal consistency timer has expired. */
// case SCARD_E_INSUFFICIENT_BUFFER : /**< The data buffer to receive returned data is too small for the returned data. */
// case SCARD_E_UNKNOWN_READER : /**< The specified reader name is not recognized. */
// case SCARD_E_TIMEOUT : /**< The user-specified timeout value has expired. */
// case SCARD_E_CANT_DISPOSE : /**< The system could not dispose of the media in the requested manner. */
// case SCARD_E_INVALID_VALUE : /**< One or more of the supplied parameters values could not be properly interpreted. */
// case SCARD_E_SYSTEM_CANCELLED : /**< The action was cancelled by the system, presumably to log off or shut down. */
// case SCARD_F_COMM_ERROR : /**< An internal communications error has been detected. */
// case SCARD_F_UNKNOWN_ERROR : /**< An internal error has been detected, but the source is unknown. */
// case SCARD_E_INVALID_ATR : /**< An ATR obtained from the registry is not a valid ATR string. */
// case SCARD_E_NOT_TRANSACTED : /**< An attempt was made to end a non-existent transaction. */
// case SCARD_E_READER_UNAVAILABLE : /**< The specified reader is not currently available for use. */
// case SCARD_W_UNSUPPORTED_CARD : /**< The reader cannot communicate with the card, due to ATR string configuration conflicts. */
// case SCARD_W_RESET_CARD : /**< The smart card has been reset, so any shared state information is invalid. */
// case SCARD_E_PCI_TOO_SMALL : /**< The PCI Receive buffer was too small. */
// case SCARD_E_READER_UNSUPPORTED : /**< The reader driver does not meet minimal requirements for support. */
// case SCARD_E_DUPLICATE_READER : /**< The reader driver did not produce a unique reader name. */
// case SCARD_E_CARD_UNSUPPORTED : /**< The smart card does not meet minimal requirements for support. */
// case SCARD_E_NO_SERVICE : /**< The Smart card resource manager is not running. */
// case SCARD_E_SERVICE_STOPPED : /**< The Smart card resource manager has shut down. */
// case SCARD_E_NO_READERS_AVAILABLE : /**< Cannot find a smart card reader. */
default:
fprintf(stderr, "HW_ERROR\n");
return 0xFB;
}
}
__u8 get_bStatus(LONG pcsc_result)
{
__u8 bStatus = 0;
if (rstate.dwEventState & SCARD_STATE_PRESENT) {
if (rstate.dwEventState & SCARD_STATE_MUTE ||
rstate.dwEventState & SCARD_STATE_UNPOWERED) {
// inactive
fprintf(stderr, "card inactive\n");
bStatus = 1;
} else {
// active
fprintf(stderr, "card active\n");
bStatus = 0;
}
} else {
// absent
fprintf(stderr, "card absent\n");
bStatus = 2;
if (hcard != 0) {
pcsc_result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
hcard = 0;
}
}
if (pcsc_result != SCARD_S_SUCCESS) {
bStatus |= (1<<6);
fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(pcsc_result));
}
return bStatus;
}
RDR_to_PC_SlotStatus_t get_RDR_to_PC_SlotStatus(__u8 bSlot, __u8 bSeq,
LONG pcsc_result)
{
RDR_to_PC_SlotStatus_t result;
result.bMessageType = 0x81;
result.dwLength = __constant_cpu_to_le32(0);
result.bSlot = bSlot;
result.bSeq = bSeq;
result.bStatus = get_bStatus(pcsc_result);
result.bError = get_bError(pcsc_result);
result.bClockStatus = 0;
return result;
}
RDR_to_PC_DataBlock_t get_RDR_to_PC_DataBlock(__u8 bSlot, __u8 bSeq,
LONG pcsc_result, __le32 dwLength)
{
RDR_to_PC_DataBlock_t result;
result.bMessageType = 0x80;
result.dwLength = dwLength;
result.bSlot = bSlot;
result.bSeq = bSeq;
result.bStatus = get_bStatus(pcsc_result);
result.bError = get_bError(pcsc_result);
result.bChainParameter = 0;
return result;
}
RDR_to_PC_SlotStatus_t
perform_PC_to_RDR_GetSlotStatus(const PC_to_RDR_GetSlotStatus_t request)
{
if ( request.bMessageType != 0x65 ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.bSlot != 0 ||
request.abRFU1 != 0 ||
request.abRFU2 != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_GetSlotStatus\n");
return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq,
SCardGetStatusChange(hcontext, 1, &rstate, 1));
}
RDR_to_PC_SlotStatus_t
perform_PC_to_RDR_IccPowerOn(const PC_to_RDR_IccPowerOn_t request, char ** pATR)
{
if ( request.bMessageType != 0x62 ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.bSlot != 0 ||
!( request.bPowerSelect == 0 ||
request.bPowerSelect & ccid_desc.bVoltageSupport ) ||
request.abRFU != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOn\n");
LONG pcsc_result;
if (hcard) {
pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE,
SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD,
&dwActiveProtocol);
} else {
pcsc_result = SCardConnect(hcontext, reader_name,
SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,
&hcard, &dwActiveProtocol);
}
if (pcsc_result == SCARD_S_SUCCESS)
pcsc_result = SCardGetStatusChange(hcontext, 1, &rstate, 1);
RDR_to_PC_SlotStatus_t result = get_RDR_to_PC_SlotStatus(request.bSlot,
request.bSeq, pcsc_result);
if (pcsc_result != SCARD_S_SUCCESS) {
*pATR = NULL;
result.dwLength = __constant_cpu_to_le32(0);
} else {
*pATR = (char*) rstate.rgbAtr;
result.dwLength = __cpu_to_le32(rstate.cbAtr);
}
return result;
}
RDR_to_PC_SlotStatus_t
perform_PC_to_RDR_IccPowerOff(const PC_to_RDR_IccPowerOff_t request)
{
if ( request.bMessageType != 0x63 ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.bSlot != 0 ||
request.abRFU1 != 0 ||
request.abRFU2 != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOff\n");
LONG result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
hcard = 0;
if (result == SCARD_E_INVALID_HANDLE) {
result = SCardGetStatusChange(hcontext, 1, &rstate, 1);
}
return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq, result);
}
RDR_to_PC_DataBlock_t
perform_PC_to_RDR_XfrBlock(const PC_to_RDR_XfrBlock_t request, const __u8*
abDataIn, __u8** abDataOut)
{
if ( request.bMessageType != 0x6F ||
request.bSlot != 0 ||
request.bBWI != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_XfrBlock\n");
DWORD dwRecvLength = MAX_BUFFER_SIZE;
*abDataOut = (__u8 *) malloc(dwRecvLength);
if (*abDataOut == NULL) {
return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0));
}
LPCSCARD_IO_REQUEST pioSendPci;
if (dwActiveProtocol == SCARD_PROTOCOL_T0)
pioSendPci = SCARD_PCI_T0;
else
pioSendPci = SCARD_PCI_T1;
int pcsc_result = SCardTransmit(hcard, pioSendPci, abDataIn,
__le32_to_cpu(request.dwLength), NULL, *abDataOut, &dwRecvLength);
return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
pcsc_result, __cpu_to_le32(dwRecvLength));
}
RDR_to_PC_Parameters_t
get_RDR_to_PC_Parameters(__u8 bSlot, __u8 bSeq, LONG pcsc_result, __u8
**abProtocolDataStructure)
{
RDR_to_PC_Parameters_t result;
result.bMessageType = 0x82;
result.bSlot = bSlot;
result.bSeq = bSeq;
if (pcsc_result == SCARD_S_SUCCESS) {
if (dwActiveProtocol == SCARD_PROTOCOL_T0) {
result.bProtocolNum = 0;
*abProtocolDataStructure = (__u8 *) malloc(sizeof
(abProtocolDataStructure_T0_t));
if (*abProtocolDataStructure) {
fprintf (stderr, "T0\n");
result.dwLength = __constant_cpu_to_le32(sizeof
(abProtocolDataStructure_T0_t));
abProtocolDataStructure_T0_t * t0 =
*(abProtocolDataStructure_T0_t**) abProtocolDataStructure;
/* values taken from ISO 7816-3 defaults
* FIXME analyze ATR to get values */
t0->bmFindexDindex = 1<<4| // index to table 7 ISO 7816-3 (Fi)
1; // index to table 8 ISO 7816-3 (Di)
t0->bmTCCKST0 = 0<<1; // convention (direct)
t0->bGuardTimeT0 = 0xFF;
t0->bWaitingIntegerT0 = 0x10;
t0->bClockStop = 0; // (not allowed)
} else {
// error malloc
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
pcsc_result = SCARD_E_INSUFFICIENT_BUFFER;
}
} else {
result.bProtocolNum = 1;
*abProtocolDataStructure = (__u8 *) malloc(sizeof
(abProtocolDataStructure_T1_t));
if (*abProtocolDataStructure) {
fprintf (stderr, "T1\n");
result.dwLength = __constant_cpu_to_le32(sizeof
(abProtocolDataStructure_T1_t));
abProtocolDataStructure_T1_t * t1 =
*(abProtocolDataStructure_T1_t**) abProtocolDataStructure;
/* values taken from OpenPGP-card
* FIXME analyze ATR to get values */
t1->bmFindexDindex = 1<<4| // index to table 7 ISO 7816-3 (Fi)
3; // index to table 8 ISO 7816-3 (Di)
t1->bmTCCKST1 = 0| // checksum type (CRC)
0<<1| // convention (direct)
0x10;
t1->bGuardTimeT1 = 0xFF;
t1->bWaitingIntegersT1 = 4<<4| // BWI
5; // CWI
t1->bClockStop = 0; // (not allowed)
t1->bIFSC = 0x80;
t1->bNadValue = 0; // see 7816-3 9.4.2.1 (only default value)
} else {
// error malloc
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
pcsc_result = SCARD_E_INSUFFICIENT_BUFFER;
}
}
} else {
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
}
result.bStatus = get_bStatus(pcsc_result);
result.bError = get_bError(pcsc_result);
return result;
}
RDR_to_PC_Parameters_t
perform_PC_to_RDR_GetParamters(const PC_to_RDR_GetParameters_t request,
__u8** abProtocolDataStructure)
{
if ( request.bMessageType != 0x6C ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.bSlot != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_GetParamters\n");
LONG pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE,
SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD,
&dwActiveProtocol);
return get_RDR_to_PC_Parameters(request.bSlot, request.bSeq,
pcsc_result, abProtocolDataStructure);
}
RDR_to_PC_NotifySlotChange_t
get_RDR_to_PC_NotifySlotChange ()
{
RDR_to_PC_NotifySlotChange_t result;
result.bMessageType = 0x50;
result.bmSlotICCState = 0; // no change
DWORD current = rstate.dwEventState;
if (SCARD_S_SUCCESS != SCardGetStatusChange(hcontext, 1, &rstate, 1)) {
fprintf(stderr, "state changed: error\n");
result.bmSlotICCState = 2; // changed (error)
} else if (!(current & rstate.dwEventState)) {
fprintf(stderr, "state changed\n");
result.bmSlotICCState = 2; // changed
}
return result;
}
RDR_to_PC_SlotStatus_t
perform_unknown(const PC_to_RDR_GetSlotStatus_t request)
{
RDR_to_PC_SlotStatus_t result;
switch (request.bMessageType) {
case 0x62:
case 0x6F:
case 0x69:
result.bMessageType = 0x80;
break;
case 0x63:
case 0x65:
case 0x6E:
case 0x6A:
case 0x71:
case 0x72:
result.bMessageType = 0x81;
break;
case 0x61:
case 0x6C:
case 0x6D:
result.bMessageType = 0x82;
break;
case 0x6B:
result.bMessageType = 0x83;
break;
case 0x73:
result.bMessageType = 0x84;
break;
default:
fprintf(stderr, "unknown message type\n");
result.bMessageType = 0;
}
result.dwLength = __constant_cpu_to_le32(0);
result.bSlot = request.bSlot,
result.bSeq = request.bSeq;
result.bStatus = get_bStatus(SCARD_F_UNKNOWN_ERROR);
result.bError = 0;
result.bClockStatus = 0;
return result;
}
int parse_ccid(const __u8* inbuf, __u8** outbuf) {
if (inbuf == NULL)
return 0;
int result = -1;
if (SCardIsValidContext(hcontext) != SCARD_S_SUCCESS) {
if (perform_initialization(reader_num) == NULL)
goto error;
}
switch (*inbuf) {
case 0x62:
{ fprintf(stderr, "PC_to_RDR_IccPowerOn\n");
char* atr;
PC_to_RDR_IccPowerOn_t input =
*(PC_to_RDR_IccPowerOn_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_PC_to_RDR_IccPowerOn(input, &atr);
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, atr,
__le32_to_cpu(output.dwLength));
} break;
case 0x63:
{ fprintf(stderr, "PC_to_RDR_IccPowerOff\n");
PC_to_RDR_IccPowerOff_t input =
*(PC_to_RDR_IccPowerOff_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_PC_to_RDR_IccPowerOff(input);
result = sizeof output;
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
} break;
case 0x65:
{ fprintf(stderr, "PC_to_RDR_GetSlotStatus\n");
PC_to_RDR_GetSlotStatus_t input =
*(PC_to_RDR_GetSlotStatus_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_PC_to_RDR_GetSlotStatus(input);
result = sizeof output;
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
} break;
case 0x6F:
{ fprintf(stderr, "PC_to_RDR_XfrBlock\n");
__u8* rapdu;
PC_to_RDR_XfrBlock_t input = *(PC_to_RDR_XfrBlock_t*) inbuf;
RDR_to_PC_DataBlock_t output =
perform_PC_to_RDR_XfrBlock(input, inbuf + sizeof input,
&rapdu);
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
free(rapdu);
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, rapdu,
__le32_to_cpu(output.dwLength));
free(rapdu);
} break;
case 0x6C:
{ fprintf(stderr, "PC_to_RDR_GetParameters\n");
__u8* abProtocolDataStructure;
PC_to_RDR_GetParameters_t input = *(PC_to_RDR_GetParameters_t*)
inbuf;
RDR_to_PC_Parameters_t output = perform_PC_to_RDR_GetParamters(
input, &abProtocolDataStructure );
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
if (abProtocolDataStructure)
free(abProtocolDataStructure);
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, abProtocolDataStructure,
__le32_to_cpu(output.dwLength));
if (abProtocolDataStructure)
free(abProtocolDataStructure);
} break;
default:
error:
{ fprintf(stderr, "unknown ccid command: 0x%4X\n", *inbuf);
PC_to_RDR_GetSlotStatus_t input =
*(PC_to_RDR_GetSlotStatus_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_unknown(input);
result = sizeof output;
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
}
}
return result;
}
int parse_ccid_control(struct usb_ctrlrequest *setup, __u8 **outbuf) {
int result = -1;
__u16 value, index, length;
value = __le16_to_cpu(setup->wValue);
index = __le16_to_cpu(setup->wIndex);
length = __le16_to_cpu(setup->wLength);
if (setup->bRequestType == USB_REQ_CCID)
switch(setup->bRequest) {
case CCID_CONTROL_ABORT:
{
fprintf(stderr, "ABORT\n");
if (length != 0x00) {
fprintf(stderr, "warning: malformed ABORT\n");
}
result = SCardCancel(hcontext);
if (result != SCARD_S_SUCCESS)
fprintf(stderr, "pc/sc error: %s\n",
pcsc_stringify_error(result));
result = 0;
} break;
case CCID_CONTROL_GET_CLOCK_FREQUENCIES:
{
fprintf(stderr, "GET_CLOCK_FREQUENCIES\n");
if (value != 0x00) {
fprintf(stderr,
"warning: malformed GET_CLOCK_FREQUENCIES\n");
}
result = sizeof(__le32);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
__le32 clock = ccid_desc.dwDefaultClock;
memcpy(*outbuf, &clock, sizeof (__le32));
} break;
case CCID_CONTROL_GET_DATA_RATES:
{
fprintf(stderr, "GET_DATA_RATES\n");
if (value != 0x00) {
fprintf(stderr, "warning: malformed GET_DATA_RATES\n");
}
result = sizeof (__le32);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
__le32 drate = ccid_desc.dwDataRate;
memcpy(*outbuf, &drate, sizeof (__le32));
} break;
}
return result;
}

145
ccid/usbstring.c Normal file
View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2003 David Brownell
*
* This program 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.
*/
#include <errno.h>
#include <string.h>
#include <linux/types.h>
#include <linux/usb/ch9.h>
#include <stdio.h>
#include "usbstring.h"
static inline void put_unaligned_le16(__u16 val, __u16 *cp)
{
__u8 *p = (void *)cp;
*p++ = (__u8) val;
*p++ = (__u8) (val >> 8);
}
static int utf8_to_utf16le(const char *s, __u16 *cp, unsigned len)
{
int count = 0;
__u8 c;
__u16 uchar;
/* this insists on correct encodings, though not minimal ones.
* BUT it currently rejects legit 4-byte UTF-8 code points,
* which need surrogate pairs. (Unicode 3.1 can use them.)
*/
while (len != 0 && (c = (__u8) *s++) != 0) {
if (c & 0x80) {
// 2-byte sequence:
// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
if ((c & 0xe0) == 0xc0) {
uchar = (c & 0x1f) << 6;
c = (__u8) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c;
// 3-byte sequence (most CJKV characters):
// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
} else if ((c & 0xf0) == 0xe0) {
uchar = (c & 0x0f) << 12;
c = (__u8) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c << 6;
c = (__u8) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c;
/* no bogus surrogates */
if (0xd800 <= uchar && uchar <= 0xdfff)
goto fail;
// 4-byte sequence (surrogate pairs, currently rare):
// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
// = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
// (uuuuu = wwww + 1)
// FIXME accept the surrogate code points (only)
} else
goto fail;
} else
uchar = c;
put_unaligned_le16 (uchar, cp++);
count++;
len--;
}
return count;
fail:
return -1;
}
/**
* usb_gadget_get_string - fill out a string descriptor
* @table: of c strings encoded using UTF-8
* @id: string id, from low byte of wValue in get string descriptor
* @buf: at least 256 bytes
*
* Finds the UTF-8 string matching the ID, and converts it into a
* string descriptor in utf16-le.
* Returns length of descriptor (always even) or negative errno
*
* If your driver needs strings in multiple languages, you'll probably
* "switch (wIndex) { ... }" in your ep0 string descriptor logic,
* using this routine after choosing which set of UTF-8 strings to use.
*
* Note that US-ASCII is a strict subset of UTF-8; any string bytes with
* the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
* characters.
*/
int
usb_gadget_get_string (struct usb_gadget_strings *table, int id, __u8 *buf)
{
struct usb_string *s;
int len;
/* descriptor 0 has the language id */
if (id == 0) {
buf [0] = 4;
buf [1] = USB_DT_STRING;
buf [2] = (__u8) table->language;
buf [3] = (__u8) (table->language >> 8);
return 4;
}
for (s = table->strings; s && s->s; s++) {
if (s->id == id)
break;
}
/* unrecognized: stall. */
if (!s || !s->s)
return -EINVAL;
/* string descriptors have length, tag, then UTF16-LE text */
len = strlen (s->s);
if (len > 126)
len = 126;
memset (buf + 2, 0, 2 * len); /* zero all the bytes */
len = utf8_to_utf16le(s->s, (__u16 *)&buf[2], len);
if (len < 0)
return -EINVAL;
buf [0] = (len + 1) * 2;
buf [1] = USB_DT_STRING;
return buf [0];
}

38
ccid/usbstring.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* (c) Copyright 2003 by David Brownell
* All Rights Reserved.
*
* This software is licensed under the GNU LGPL version 2.
*/
/* utility to simplify dealing with string descriptors */
/**
* struct usb_string - wraps a C string and its USB id
* @id: the (nonzero) ID for this string
* @s: the string, in UTF-8 encoding
*
* If you're using usb_gadget_get_string(), use this to wrap a string
* together with its ID.
*/
struct usb_string {
__u8 id;
const char *s;
};
/**
* struct usb_gadget_strings - a set of USB strings in a given language
* @language: identifies the strings' language (0x0409 for en-us)
* @strings: array of strings with their ids
*
* If you're using usb_gadget_get_string(), use this to wrap all the
* strings for a given language.
*/
struct usb_gadget_strings {
__u16 language; /* 0x0409 for en-us */
struct usb_string *strings;
};
/* put descriptor for string with that id into buf (buflen >= 256) */
int usb_gadget_get_string (struct usb_gadget_strings *table, int id, __u8 *buf);

108
virtualsmartcard/Makefile Normal file
View File

@@ -0,0 +1,108 @@
MAKEFLAGS += -rR --no-print-directory
# Directories
prefix =
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
serialdropdir = `pkg-config libpcsclite --variable=usbdropdir`/serial
sysconfdir = $(prefix)/etc
readerconfdir = $(sysconfdir)/reader.conf.d
python_sitelib = /usr/lib/python2.5/site-packages
# Compiler
CC = gcc
CFLAGS = -Wall -g
LIBPCSCLITE_CFLAGS = `pkg-config --cflags --libs libpcsclite`
# use -fPIC in case of problems
SO_CFLAGS = -fpic
# Linker
LD = ld
LDFLAGS = -G
INSTALL = install
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL) -m 644
UPDATEREADERCONF = update-reader.conf
RESTARTPCSCD = /etc/init.d/pcscd restart
TARGETS = libvpcd.so configuration virtualsmartcard
# top-level rule
all: $(TARGETS)
libvpcd.so: vpcd/vpcd.c vpcd/vpcd.h
$(CC) $(LIBPCSCLITE_CFLAGS) $(SO_CFLAGS) $(CFLAGS) -c $< -o $(@:%.so=%.o)
$(LD) $(LDFLAGS) $(@:%.so=%.o) -o $@ -soname=$@.1 -lc
rm -f $(@:%.so=%.o)
.PHONY: virtualsmartcard
virtualsmartcard:
echo '#!/bin/sh' > virtualsmartcard
echo 'cd "$(python_sitelib)"' \
>> virtualsmartcard
echo 'python "$(python_sitelib)/VirtualSmartcard.py" "$$@"' \
>> virtualsmartcard
.PHONY: configuration
configuration:
echo 'FRIENDLYNAME "Virtual PCD"' > vpcd.conf
echo 'DEVICENAME /dev/null' >> vpcd.conf
echo "LIBPATH $(serialdropdir)/libvpcd.so" >> vpcd.conf
echo 'CHANNELID 0' >> vpcd.conf
install: $(TARGETS) installdirs
$(INSTALL_PROGRAM) libvpcd.so $(DESTDIR)$(serialdropdir)
$(INSTALL_PROGRAM) virtualsmartcard $(DESTDIR)$(bindir)
$(INSTALL_DATA) vpcd.conf $(DESTDIR)$(readerconfdir)
$(INSTALL_DATA) vpicc/ConstantDefinitions.py $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/CryptoUtils.py $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/SEutils.py $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/SmartcardFilesystem.py $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/SmartcardSAM.py $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/SWutils.py $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/TLVutils.py $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/utils.py $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/VirtualSmartcard.py $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/testconfig.sam $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/testconfig.mf $(DESTDIR)$(python_sitelib)
$(INSTALL_DATA) vpicc/jp2.jpg $(DESTDIR)$(python_sitelib)
.PHONY: installdirs
installdirs:
$(INSTALL) -d $(DESTDIR)$(serialdropdir)
$(INSTALL) -d $(DESTDIR)$(bindir)
$(INSTALL) -d $(DESTDIR)$(readerconfdir)
$(INSTALL) -d $(DESTDIR)$(python_sitelib)
.PHONY: install-strip
install-strip:
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
.PHONY: uninstall
uninstall:
rm -f $(DESTDIR)$(serialdropdir)/libvpcd.so
rm -f $(DESTDIR)$(bindir)/virtualsmartcard
rm -f $(DESTDIR)$(readerconfdir)/vpcd.conf
rm -f $(DESTDIR)$(python_sitelib)/ConstantDefinitions.py
rm -f $(DESTDIR)$(python_sitelib)/CryptoUtils.py
rm -f $(DESTDIR)$(python_sitelib)/SEutils.py
rm -f $(DESTDIR)$(python_sitelib)/SmartcardFilesystem.py
rm -f $(DESTDIR)$(python_sitelib)/SmartcardSAM.py
rm -f $(DESTDIR)$(python_sitelib)/SWutils.py
rm -f $(DESTDIR)$(python_sitelib)/TLVutils.py
rm -f $(DESTDIR)$(python_sitelib)/utils.py
rm -f $(DESTDIR)$(python_sitelib)/VirtualSmartcard.py
rm -f $(DESTDIR)$(python_sitelib)/testconfig.sam
rm -f $(DESTDIR)$(python_sitelib)/testconfig.mf
rm -f $(DESTDIR)$(python_sitelib)/jp2.jpg
.PHONY: clean
clean:
rm -f libvpcd.so
rm -f vpcd.conf
rm -f virtualsmartcard

View File

@@ -0,0 +1,69 @@
DESCRIPTION = "Virtual Smartcard with PCSC Driver and CCID to PCSC Gadget"
LICENSE = "GPL"
DEPENDS = "pcsc-lite"
RDEPENDS = "pcsc-lite python-pycrypto python-crypt python-textutils python-imaging python-pickle"
SRC_URI = "svn://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard/;module=virtualsmartcard;proto=https;rev=1"
S = "${WORKDIR}"
serialdropdir = ${libdir}/pcsc/drivers/serial
readerconfdir = ${sysconfdir}/reader.conf.d
python_sitelib = /usr/lib/python2.5/site-packages
LIBPCSCLITE_CFLAGS = -I${STAGING_INCDIR}/PCSC -lpcsclite
SO_CFLAGS = -fPIC
SO_LDFLAGS = -shared
INSTALL = install
INSTALL_PROGRAM = ${INSTALL}
INSTALL_DATA = ${INSTALL} -m 644
FILES_${PN} = "\
${bindir}/* \
${readerconfdir}/* \
${serialdropdir}/* \
${python_sitelib}/* \
"
do_compile() {
${CC} -c ${S}/virtualsmartcard/vpcd/vpcd.c \
${S}/virtualsmartcard/vpcd/vpcd.h \
${LIBPCSCLITE_CFLAGS} ${SO_CFLAGS} ${CFLAGS}
${CC} ${S}/vpcd.o -o ${S}/virtualsmartcard/libvpcd.so \
${LIBPCSCLITE_CFLAGS} ${SO_LDFLAGS} ${LDFLAGS}
rm -f ${S}/vpcd.o
echo '#!/bin/sh' > ${S}/virtualsmartcard/virtualsmartcard
echo "cd \"${python_sitelib}\"" \
>> ${S}/virtualsmartcard/virtualsmartcard
echo "python \"${python_sitelib}/VirtualSmartcard.py\" \"\$@\"" \
>> ${S}/virtualsmartcard/virtualsmartcard
echo 'FRIENDLYNAME "Virtual PCD"' > ${S}/virtualsmartcard/vpcd.conf
echo 'DEVICENAME /dev/null' >> ${S}/virtualsmartcard/vpcd.conf
echo "LIBPATH ${serialdropdir}/libvpcd.so" >> ${S}/virtualsmartcard/vpcd.conf
echo 'CHANNELID 0' >> ${S}/virtualsmartcard/vpcd.conf
}
do_install() {
${INSTALL} -d ${D}${bindir}
${INSTALL} -d ${D}${serialdropdir}
${INSTALL} -d ${D}${readerconfdir}
${INSTALL} -d ${D}${python_sitelib}
${INSTALL_PROGRAM} ${S}/virtualsmartcard/virtualsmartcard ${D}${bindir}
${INSTALL_PROGRAM} ${S}/virtualsmartcard/libvpcd.so ${D}${serialdropdir}
${INSTALL_DATA} ${S}/virtualsmartcard/vpcd.conf ${D}${readerconfdir}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/ConstantDefinitions.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/CryptoUtils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/SEutils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/SmartcardFilesystem.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/SmartcardSAM.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/SWutils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/TLVutils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/utils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/VirtualSmartcard.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/testconfig.sam ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/testconfig.mf ${D}${python_sitelib}
${INSTALL_DATA} ${S}/virtualsmartcard/vpicc/jp2.jpg ${D}${python_sitelib}
}

View File

@@ -0,0 +1,153 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ifdhandler.h>
#include <debuglog.h>
#include "vpcd.h"
/*
* List of Defined Functions Available to IFD_Handler 1.0
*/
RESPONSECODE
IO_Create_Channel(DWORD channelid)
{
if (vicc_init() < 0) return IFD_COMMUNICATION_ERROR;
return IFD_SUCCESS;
}
RESPONSECODE
IO_Close_Channel()
{
RESPONSECODE r = IFD_Eject_ICC();
if (vicc_exit() < 0) return IFD_COMMUNICATION_ERROR;
return r;
}
RESPONSECODE
IFD_Get_Capabilities(DWORD tag, PUCHAR value)
{
char * atr;
int size;
switch (tag) {
case TAG_IFD_ATR:
size = vicc_getatr(&atr);
if (size < 0) {
Log1(PCSC_LOG_ERROR, "could not get ATR");
return IFD_COMMUNICATION_ERROR;
}
value = memcpy(value, atr, size);
free(atr);
return IFD_SUCCESS;
case TAG_IFD_SLOTS_NUMBER:
(*value) = 0;
return IFD_SUCCESS;
//case TAG_IFD_POLLING_THREAD_KILLABLE:
//return IFD_SUCCESS;
default:
Log2(PCSC_LOG_DEBUG, "unknown tag %d", (int)tag);
}
return IFD_ERROR_TAG;
}
RESPONSECODE
IFD_Set_Capabilities(DWORD tag, PUCHAR value)
{
return IFD_NOT_SUPPORTED;
}
RESPONSECODE
IFD_Set_Protocol_Parameters(DWORD protocoltype, UCHAR selectionflags, UCHAR
pts1, UCHAR pts2, UCHAR pts3)
{
return IFD_SUCCESS;
}
RESPONSECODE
IFD_Power_ICC(DWORD a)
{
if ((a == IFD_POWER_DOWN) || (a == IFD_RESET)) {
if (vicc_poweroff() < 0) {
Log1(PCSC_LOG_ERROR, "could not powerdown");
return IFD_COMMUNICATION_ERROR;
}
}
if ((a == IFD_POWER_UP) || (a == IFD_RESET)) {
if (vicc_poweron() < 0) {
Log1(PCSC_LOG_ERROR, "could not powerup");
return IFD_COMMUNICATION_ERROR;
}
}
return IFD_SUCCESS;
}
RESPONSECODE
IFD_Swallow_ICC()
{
Log1(PCSC_LOG_DEBUG, "");
return IFD_NOT_SUPPORTED;
}
RESPONSECODE
IFD_Eject_ICC()
{
if (vicc_eject() < 0) {
return IFD_COMMUNICATION_ERROR;
}
return IFD_SUCCESS;
}
RESPONSECODE
IFD_Confiscate_ICC()
{
Log1(PCSC_LOG_DEBUG, "");
return IFD_NOT_SUPPORTED;
}
RESPONSECODE
IFD_Transmit_to_ICC(SCARD_IO_HEADER SendPci, PUCHAR TxBuffer, DWORD TxLength,
PUCHAR RxBuffer, PDWORD RxLength, PSCARD_IO_HEADER RecvPci)
{
(*RxLength) = 0;
char *rapdu;
int size = vicc_transmit(TxLength, (char *) TxBuffer, &rapdu);
if (size < 0) {
Log1(PCSC_LOG_ERROR, "could not send apdu or receive rapdu");
return IFD_COMMUNICATION_ERROR;
}
(*RxLength) = size;
RxBuffer = memcpy(RxBuffer, rapdu, size);
free(rapdu);
return IFD_SUCCESS;
}
RESPONSECODE
IFD_Is_ICC_Present()
{
switch (vicc_present()) {
case 0: return IFD_ICC_NOT_PRESENT;
case 1: return IFD_ICC_PRESENT;
default: return IFD_COMMUNICATION_ERROR;
}
return IFD_COMMUNICATION_ERROR;
}
RESPONSECODE
IFD_Is_ICC_Absent()
{
return IFD_Is_ICC_Present();
}

View File

@@ -0,0 +1,181 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define PORT 35963
#define LENLEN 3
int server_sock = -1;
int client_sock = -1;
/*
* Send all size bytes from buffer to sock
*/
int sendall(int sock, size_t size, const char* buffer) {
/* send a plain message */
size_t sent = 0;
int i;
while (sent < size) {
i = send(sock, buffer, size-sent, 0);
if (i < 0) return i;
sent += i;
}
return 0;
}
/*
* Receive size bytes from sock
*/
char* recvall(int sock, size_t size) {
char* buffer = (char*) malloc(size);
if (buffer == NULL) return NULL;
if (recv(sock, buffer, size, MSG_WAITALL) < size) {
free(buffer);
return NULL;
}
return buffer;
}
int vicc_eject() {
if (client_sock > 0) {
client_sock = close(client_sock);
if (client_sock < 0) {
return -1;
}
}
return 0;
}
/*
* First send length of message to the socket on LENLEN bytes, then send the
* message itself to the socket.
*/
int sendToVICC(size_t size, const char* buffer) {
/* send size of message on LENLEN bytes */
char sizebuf[LENLEN];
size_t i;
for (i = 0; i < LENLEN; i++)
sizebuf[i] = ((size>>8*(LENLEN-i-1)) & 0xff);
i = sendall(client_sock, LENLEN, sizebuf);
if (i<0) {
vicc_eject();
return i;
}
i = sendall(client_sock, size, buffer);
if (i<0) {
vicc_eject();
return i;
}
return 0;
}
/*
* Receive a message from icc
*/
int recvFromVICC(char** buffer) {
/* receive size of message on LENLEN bytes */
unsigned char* sizebuffer = (unsigned char*) recvall(client_sock, LENLEN);
if (sizebuffer == NULL) {
vicc_eject();
return -1;
}
/* calculate size */
int size = 0;
int i;
for (i = 0; i < LENLEN; i++) {
size <<= 8;
size += sizebuffer[i];
}
free(sizebuffer);
/* receive message */
*buffer = recvall(client_sock, size);
if (*buffer == NULL) {
vicc_eject();
return -1;
}
return size;
}
int vicc_init() {
server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock < 0) return -1;
struct sockaddr_in server_sockaddr;
memset(&server_sockaddr, 0, sizeof(server_sockaddr));
server_sockaddr.sin_family = PF_INET;
server_sockaddr.sin_port = htons(PORT);
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_sock, (struct sockaddr*)&server_sockaddr,
sizeof(server_sockaddr)) < 0) return -1;
if (listen(server_sock, 0) < 0) return -1;
return 0;
}
int vicc_exit() {
if (server_sock > 0) {
server_sock = close(server_sock);
if (server_sock < 0) return -1;
}
return 0;
}
int vicc_transmit(int apdu_len, const char *apdu, char **rapdu) {
if (sendToVICC(apdu_len, apdu) < 0) return -1;
return recvFromVICC(rapdu);
}
int vicc_getatr(char** atr) {
return vicc_transmit(0, "", atr);
}
int vicc_present() {
if (client_sock > 0) return 1;
else {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(server_sock, &rfds);
/* Wait up to one microsecond. */
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1;
if (select(server_sock+1, &rfds, NULL, NULL, &tv) < 0) return -1;
if (FD_ISSET(server_sock, &rfds)) {
struct sockaddr_in client_sockaddr;
socklen_t client_socklen = sizeof(client_sockaddr);
client_sock = accept(server_sock,
(struct sockaddr*)&client_sockaddr,
&client_socklen);
if (client_sock == -1) return -1;
return 1;
}
}
return 0;
}
int vicc_poweron() {
char on = 1;
return sendToVICC(1, &on);
}
int vicc_poweroff() {
char off = 0;
return sendToVICC(1, &off);
}

View File

@@ -0,0 +1,207 @@
# Life cycle status byte {{{
LCB = {}
LCB["NOINFORMATION"] = 0x00
LCB["CREATION"] = 0x01
LCB["INITIALISATION"] = 0x03
LCB["ACTIVATED"] = 0x05
LCB["DEACTIVATED"] = 0x04
LCB["TERMINATION"] = 0x0C
# }}}
# Channel security attribute {{{
CS = {}
CS["NOTSHAREABLE"] = 0x01
CS["SECURED"] = 0x02
CS["USERAUTHENTICATED"] = 0x03
# }}}
# Security attribute {{{
# Security attribute, Access mode byte for DFs/EFs {{{
SA = {}
SA["AM_DF_DELETESELF"] = SA["AM_EF_DELETEFILE"] = 0x40
SA["AM_DF_TERMINATEDF"] = SA["AM_EF_TERMINATEFILE"] = 0x20
SA["AM_DF_ACTIVATEFILE"] = SA["AM_EF_ACTIVATEFILE"] = 0x10
SA["AM_DF_DEACTIVATEFILE"] = SA["AM_EF_DEACTIVATEFILE"] = 0x08
SA["AM_DF_CREATEDF"] = SA["AM_EF_WRITEBINARY"] = 0x04
SA["AM_DF_CREATEEF"] = SA["AM_EF_UPDATEBINARY"] = 0x02
SA["AM_DF_DELETECHILD"] = SA["AM_EF_READBINARY"] = 0x01
# }}}
# Security attribute in compact format, Security condition byte {{{
SA["CF_SC_NOCONDITION"] = 0x00
SA["CF_SC_NEVER"] = 0xFF
SA["__CF_SC_ATLEASTONE"] = 0x80
SA["__CF_SC_ALLCONDITIONS"] = 0x00
SA["__CF_SC_SECUREMESSAGING"] = 0x40
SA["__CF_SC_EXTERNALAUTHENTICATION"] = 0x40
SA["__CF_SC_USERAUTHENTICATION"] = 0x40
SA["CF_SC_ONE_SECUREMESSAGING"] = SA["__CF_SC_ATLEASTONE"]|SA["__CF_SC_SECUREMESSAGING"]
SA["CF_SC_ONE_EXTERNALAUTHENTICATION"] = SA["__CF_SC_ATLEASTONE"]|SA["__CF_SC_EXTERNALAUTHENTICATION"]
SA["CF_SC_ONE_USERAUTHENTICATION"] = SA["__CF_SC_ATLEASTONE"]|SA["__CF_SC_USERAUTHENTICATION"]
SA["CF_SC_ALL_SECUREMESSAGING"] = SA["__CF_SC_ALLCONDITIONS"]|SA["__CF_SC_SECUREMESSAGING"]
SA["CF_SC_ALL_EXTERNALAUTHENTICATION"] = SA["__CF_SC_ALLCONDITIONS"]|SA["__CF_SC_EXTERNALAUTHENTICATION"]
SA["CF_SC_ALL_USERAUTHENTICATION"] = SA["__CF_SC_ALLCONDITIONS"]|SA["__CF_SC_USERAUTHENTICATION"]
# }}}
# }}}
# Data coding byte {{{
DCB = {}
DCB["ONETIMEWRITE"] = 0x00
DCB["PROPRIETARY"] = 0x20 # we use it for XOR
DCB["WRITEOR"] = 0x40
DCB["WRITEAND"] = 0x60
DCB["BERTLV_FFVALID"] = 0x10
DCB["BERTLV_FFINVALID"] = 0x10
# }}}
# File descriptor byte {{{
FDB = {}
FDB["NOTSHAREABLEFILE"] = 0x00
FDB["SHAREABLEFILE"] = 0x40
FDB["WORKINGEF"] = 0x00
FDB["INTERNALEF"] = 0x80
FDB["DF"] = 0x38
FDB["EFSTRUCTURE_NOINFORMATIONGIVEN"] = 0x00
FDB["EFSTRUCTURE_TRANSPARENT"] = 0x01
FDB["EFSTRUCTURE_LINEAR_FIXED_NOFURTHERINFO"] = 0x02
FDB["EFSTRUCTURE_LINEAR_FIXED_SIMPLETLV"] = 0x03
FDB["EFSTRUCTURE_LINEAR_VARIABLE_NOFURTHERINFO"] = 0x04
FDB["EFSTRUCTURE_LINEAR_VARIABLESIMPLETLV"] = 0x05
FDB["EFSTRUCTURE_CYCLIC_NOFURTHERINFO"] = 0x06
FDB["EFSTRUCTURE_CYCLIC_SIMPLETLV"] = 0x07
# }}}
# File identifier {{{
FID = {}
FID["EFDIR"] = 0x2F00
FID["EFATR"] = 0x2F00
FID["MF"] = 0x3F00
FID["PATHSELECTION"] = 0x3FFF
FID["RESERVED"] = 0x3FFF
# }}}
#SM constants {{{
SM_Class = {}
#Basic secure messaging objects
#'80', '81' Plain value not encoded in BER-TLV
SM_Class["PLAIN_VALUE_NO_TLV"] = 0x80
SM_Class["PLAIN_VALUE_NO_TLV_ODD"] = 0x81
# '82', '83' Cryptogram (plain value encoded in BER-TLV and including SM data objects, i.e., an SM field)
SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM"] = 0x82
SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM_ODD"] = 0x83
#'84', '85' Cryptogram (plain value encoded in BER-TLV, but not including SM data objects)
SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM"] = 0x84
SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM_ODD"] = 0x85
#'86', '87' Padding-content indicator byte followed by cryptogram (plain value not encoded in BER-TLV)
SM_Class["CRYPTOGRAM_PADDING_INDICATOR"] = 0x86
SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"] = 0x87
#'89' Command header (CLA INS P1 P2, four bytes)
SM_Class["PLAIN_COMMAND_HEADER"] = 0x89
# '8E' Cryptographic checksum (at least four bytes)
SM_Class["CHECKSUM"] = 0x8E
# '90', '91' Hash-code
SM_Class["HASH_CODE"] = 0x90
SM_Class["HASH_CODE_ODD"] = 0x91
#'92', '93' Certificate (data not encoded in BER-TLV)
SM_Class["CERTIFICATE"] = 0x92
SM_Class["CERTIFICATE_ODD"] = 0x93
#'94', '95' Security environment identifier (SEID byte)
SM_Class["SECURITY_ENIVRONMENT_ID"] = 0x94
SM_Class["SECURITY_ENIVRONMENT_ID_ODD"] = 0x95
#'96', '97' One or two bytes encoding Ne in the unsecured command-response pair (possibly empty)
SM_Class["Ne"] = 0x96
SM_Class["Ne_ODD"] = 0x97
#'99' Processing status (SW1-SW2, two bytes; possibly empty)
SM_Class["PLAIN_PROCESSING_STATUS"] = 0x99
# '9A', '9B' Input data element for the computation of a digital signature (the value field is signed)
SM_Class["INPUT_DATA_SIGNATURE_COMPUTATION"] = 0x9A
SM_Class["INPUT_DATA_SIGNATURE_COMPUTATION_ODD"] = 0x9B
# '9C', '9D' Public key
SM_Class["PUBLIC_KEY"] = 0x9C
SM_Class["PUBLIC_KEY_ODD"] = 0x9D
# '9E' Digital signature
SM_Class["DIGITAL_SIGNATURE"] = 0x9E
#Auxiliary secure messaging objects
#Input template for the computation of a hash-code (the template is hashed)
SM_Class["HASH_COMPUTATION_TEMPLATE"] = 0xA0
SM_Class["HASH_COMPUTATION_TEMPLATE_ODD"] = 0xA1
#Input template for the verification of a cryptographic checksum (the template is included)
SM_Class["CHECKSUM_VERIFICATION_TEMPLATE"] = 0xA2
#Control reference template for authentication (AT)
SM_Class["AUTH_CRT"] = 0xA4
SM_Class["AUTH_CRT_ODD"] = 0xA5
#Control reference template for key agreement (KAT)
SM_Class["KEY_AGREEMENT_CRT"] = 0xA6
SM_Class["KEY_AGREEMENT_CRT_ODD"] = 0xA7
#Input template for the verification of a digital signature (the template is signed)
SM_Class[0xA8] = "SIGNATURE_VERIFICATION_TEMPLATE"
#Control reference template for hash-code (HT)
SM_Class["HASH_CRT"] = 0xAA
SM_Class["HASH_CRT_ODD"] = 0xAB
#Input template for the computation of a digital signature (the concatenated value fields are signed)
SM_Class["SIGNATURE_COMPUTATION_TEMPLATE"] = 0xAC
SM_Class["SIGNATURE_COMPUTATION_TEMPLATE_ODD"] = 0xAD
#Input template for the verification of a certificate (the concatenated value fields are certified)
SM_Class["CERTIFICATE_VERIFICATION_TEMPLATE"] = 0xAE
SM_Class["CERTIFICATE_VERIFICATION_TEMPLATE_ODD"] = 0xAF
#Plain value encoded in BER-TLV and including SM data objects, i.e., an SM field
SM_Class["PLAIN_VALUE_TLV_INCULDING_SM"] = 0xB0
SM_Class["PLAIN_VALUE_TLV_INCULDING_SM_ODD"] = 0xB1
#Plain value encoded in BER-TLV, but not including SM data objects
SM_Class["PLAIN_VALUE_TLV_NO_SM"] = 0xB2
SM_Class["PLAIN_VALUE_TLV_NO_SM_ODD"] = 0xB3
#Control reference template for cryptographic checksum (CCT)
SM_Class["CHECKSUM_CRT"] =0xB4
SM_Class["CHECKSUM_CRT_ODD"] =0xB5
#Control reference template for digital signature (DST)
SM_Class["SIGNATURE_CRT"] = 0xB6
SM_Class["SIGNATURE_CRT_ODD"] = 0xB7
#Control reference template for confidentiality (CT)
SM_Class["CONFIDENTIALITY_CRT"] = 0xB8
SM_Class["CONFIDENTIALITY_CRT_ODD"] = 0xB9
#Response descriptor template
SM_Class["RESPONSE_DESCRIPTOR_TEMPLATE"] = 0xBA
SM_Class["RESPONSE_DESCRIPTOR_TEMPLATE_ODD"] = 0xBB
#Input template for the computation of a digital signature (the template is signed)
SM_Class["SIGNATURE_COMPUTATION_TEMPLATE"] = 0xBC
SM_Class["SIGNATURE_COMPUTATION_TEMPLATE_ODD"] = 0xBD
#Input template for the verification of a certificate (the template is certified)
SM_Class["CERTIFICATE_VERIFICATION_TEMPLATE"] = 0xBE
#}}}
#This table maps algorithms to numbers. It is used in the security environment
ALGO_MAPPING = {}
ALGO_MAPPING[0x00] = "DES3-ECB"
ALGO_MAPPING[0x01] = "DES3-CBC"
ALGO_MAPPING[0x02] = "AES-ECB"
ALGO_MAPPING[0x03] = "AES-CBC"
ALGO_MAPPING[0x04] = "DES-ECB"
ALGO_MAPPING[0x05] = "DES-CBC"
ALGO_MAPPING[0x06] = "MD5"
ALGO_MAPPING[0x07] = "SHA"
ALGO_MAPPING[0x08] = "SHA256"
ALGO_MAPPING[0x09] = "MAC"
ALGO_MAPPING[0x0A] = "HMAC"
ALGO_MAPPING[0x0B] = "CC"
ALGO_MAPPING[0x0C] = "RSA"
ALGO_MAPPING[0x0D] = "DSA"
#Tags of control reference templates (CRT)
TEMPLATE_AT = 0xA4
TEMPLATE_KAT = 0xA6
TEMPLATE_HT = 0xAA
TEMPLATE_CCT = 0xB4 # Template for Cryptographic Checksum
TEMPLATE_DST = 0xB6
TEMPLATE_CT = 0xB8 # Template for Confidentiality
# Recerence control for select command, and record handling commands {{{
REF = {
"IDENTIFIER_FIRST" : 0x00,
"IDENTIFIER_LAST" : 0x01,
"IDENTIFIER_NEXT" : 0x02,
"IDENTIFIER_PREVIOUS" : 0x03,
"IDENTIFIER_CONTROL" : 0x03,
"NUMBER" : 0x04,
"NUMBER_TO_LAST" : 0x05,
"NUMBER_FROM_LAST" : 0x06,
"NUMBER_CONTROL" : 0x07,
"REFERENCE_CONTROL" : 0x07,
}
# }}}

View File

@@ -0,0 +1,538 @@
import sys, binascii, utils, random
from Crypto.Cipher import DES3, DES, AES, ARC4 #,IDEA no longer present in python-crypto?
from struct import pack
from binascii import b2a_hex
from random import randint
from utils import inttostring, stringtoint, hexdump
import string
try:
# Use PyCrypto (if available)
from Crypto.Hash import HMAC, SHA as SHA1
except ImportError:
# PyCrypto not available. Use the Python standard library.
import hmac as HMAC
import sha as SHA1
iv = '\x00' * 8
PADDING = '\x80' + '\x00' * 7
## *******************************************************************
## * Generic methods *
## *******************************************************************
def get_cipher(cipherspec, key, iv = None):
cipherparts = cipherspec.split("-")
if len(cipherparts) > 2:
raise ValueError, 'cipherspec must be of the form "cipher-mode" or "cipher"'
elif len(cipherparts) == 1:
cipherparts[1] = "ecb"
c_class = globals().get(cipherparts[0].upper(), None)
if c_class is None:
raise ValueError, "Cipher '%s' not known, must be one of %s" % (cipherparts[0], ", ".join([e.lower() for e in dir() if e.isupper()]))
mode = getattr(c_class, "MODE_" + cipherparts[1].upper(), None)
if mode is None:
raise ValueError, "Mode '%s' not known, must be one of %s" % (cipherparts[1], ", ".join([e.split("_")[1].lower() for e in dir(c_class) if e.startswith("MODE_")]))
cipher = None
if iv is None:
cipher = c_class.new(key, mode)
else:
cipher = c_class.new(key, mode, iv)
return cipher
def append_padding(cipherspec, data, padding_class=0x01,keylength = 16):
"""Append padding to the data.
Length of padding depends on length of data and the block size of the
specified encryption algorithm.
Different types of padding may be selected via the padding_class parameter
"""
key = "DUMMYKEY" * (keylength / 8) #Key doesn't matter for padding
cipher = get_cipher(cipherspec,key,iv)
if padding_class == 0x01: #ISO padding
last_block_length = len(data) % cipher.block_size
padding_length = cipher.block_size - last_block_length
if padding_length == 0:
padding = PADDING
else:
padding = PADDING[:padding_length]
del cipher
return data + padding
def strip_padding(cipherspec,data,padding_class=0x01,keylength = 16):
"""
Strip the padding of decrypted data. Returns data without padding
"""
key = "DUMMYKEY" * (keylength / 8) #Key doesn't matter for padding
cipher = get_cipher(cipherspec,key,iv)
if padding_class == 0x01:
tail = len(data) - 1
while data[tail] != '\x80':
tail = tail - 1
return data[:tail]
def crypto_checksum(algo,key,data,iv=None,ssc=None):
if algo not in ("HMAC","MAC","CC"):
raise ValueError, "Unknown Algorithm %s" % algo
if algo == "MAC":
checksum = calculate_MAC(key,data,0x00,iv) #FIXME: IV?
elif algo == "HMAC":
hmac = HMAC.new(key,data)
checksum = hmac.hexdigest()
del hmac
elif algo == "CC":
if ssc != None:
data = inttostring(ssc) + data
a = cipher(True, "des-cbc", key[:8], data)
b = cipher(False, "des-ecb", key[8:16], a[-8:])
c = cipher(True, "des-ecb", key[:8], b)
checksum = c
return checksum
def cipher(do_encrypt, cipherspec, key, data, iv = None):
"""Do a cryptographic operation.
operation = do_encrypt ? encrypt : decrypt,
cipherspec must be of the form "cipher-mode", or "cipher\""""
cipher = get_cipher(cipherspec,key,iv)
result = None
if do_encrypt:
result = cipher.encrypt(data)
else:
result = cipher.decrypt(data)
del cipher
return result
def hash(hashmethod,data):
from Crypto.Hash import SHA, MD5#, RIPEMD
hash_class = locals().get(hashmethod.upper(), None)
if hash_class == None:
print "Unknown Hash method %s" % hashmethod
raise ValueError
hash = hash_class.new()
hash.update(data)
return hash.digest()
def operation_on_string(string1, string2, op):
if len(string1) != len(string2):
raise ValueError, "string1 and string2 must be of equal length"
result = []
for i in range(len(string1)):
result.append( chr(op(ord(string1[i]),ord(string2[i]))) )
return "".join(result)
## *******************************************************************
## * Cyberflex specific methods *
## *******************************************************************
def verify_card_cryptogram(session_key, host_challenge,
card_challenge, card_cryptogram):
message = host_challenge + card_challenge
expected = calculate_MAC(session_key, message, iv)
print >>sys.stderr, "Original: %s" % binascii.b2a_hex(card_cryptogram)
print >>sys.stderr, "Expected: %s" % binascii.b2a_hex(expected)
return card_cryptogram == expected
def calculate_host_cryptogram(session_key, card_challenge,
host_challenge):
message = card_challenge + host_challenge
return calculate_MAC(session_key, message, iv)
def calculate_MAC(session_key, message, iv):
print >>sys.stderr, "Doing MAC for: %s" % utils.hexdump(message, indent = 17)
cipher = DES3.new(session_key, DES3.MODE_CBC, iv)
block_count = len(message) / cipher.block_size
for i in range(block_count):
cipher.encrypt(message[i*cipher.block_size:(i+1)*cipher.block_size])
last_block_length = len(message) % cipher.block_size
last_block = (message[len(message)-last_block_length:]+PADDING)[:cipher.block_size]
return cipher.encrypt( last_block )
def get_derivation_data(host_challenge, card_challenge):
return card_challenge[4:8] + host_challenge[:4] + \
card_challenge[:4] + host_challenge[4:8]
def get_session_key(auth_key, host_challenge, card_challenge):
cipher = DES3.new(auth_key, DES3.MODE_ECB)
return cipher.encrypt(get_derivation_data(host_challenge, card_challenge))
def generate_host_challenge():
random.seed()
return "".join([chr(random.randint(0,255)) for e in range(8)])
def andstring(string1, string2):
return operation_on_string(string1, string2, lambda a,b: a & b)
###########################################################################
# PBKDF2.py - PKCS#5 v2.0 Password-Based Key Derivation
#
# Copyright (C) 2007, 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
# provided that the above copyright notice appear in all copies and that
# both that copyright notice and this permission notice appear in
# supporting documentation.
#
# THE AUTHOR PROVIDES THIS SOFTWARE ``AS IS'' AND ANY EXPRESSED OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Country of origin: Canada
#
###########################################################################
# Sample PBKDF2 usage:
# from Crypto.Cipher import AES
# from PBKDF2 import PBKDF2
# import os
#
# salt = os.urandom(8) # 64-bit salt
# key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
# iv = os.urandom(16) # 128-bit IV
# cipher = AES.new(key, AES.MODE_CBC, iv)
# ...
#
# Sample crypt() usage:
# from PBKDF2 import crypt
# pwhash = crypt("secret")
# alleged_pw = raw_input("Enter password: ")
# if pwhash == crypt(alleged_pw, pwhash):
# print "Password good"
# else:
# print "Invalid password"
#
###########################################################################
# History:
#
# 2007-07-27 Dwayne C. Litzenberger <dlitz@dlitz.net>
# - Initial Release (v1.0)
#
# 2007-07-31 Dwayne C. Litzenberger <dlitz@dlitz.net>
# - Bugfix release (v1.1)
# - SECURITY: The PyCrypto XOR cipher (used, if available, in the _strxor
# function in the previous release) silently truncates all keys to 64
# bytes. The way it was used in the previous release, this would only be
# problem if the pseudorandom function that returned values larger than
# 64 bytes (so SHA1, SHA256 and SHA512 are fine), but I don't like
# anything that silently reduces the security margin from what is
# expected.
#
# 2008-06-17 Dwayne C. Litzenberger <dlitz@dlitz.net>
# - Compatibility release (v1.2)
# - Add support for older versions of Python (2.2 and 2.3).
#
###########################################################################
__version__ = "1.2"
def strxor(a, b):
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
def b64encode(data, chars="+/"):
tt = string.maketrans("+/", chars)
return data.encode('base64').replace("\n", "").translate(tt)
class PBKDF2(object):
"""PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
This implementation takes a passphrase and a salt (and optionally an
iteration count, a digest module, and a MAC module) and provides a
file-like object from which an arbitrarily-sized key can be read.
If the passphrase and/or salt are unicode objects, they are encoded as
UTF-8 before they are processed.
The idea behind PBKDF2 is to derive a cryptographic key from a
passphrase and a salt.
PBKDF2 may also be used as a strong salted password hash. The
'crypt' function is provided for that purpose.
Remember: Keys generated using PBKDF2 are only as strong as the
passphrases they are derived from.
"""
def __init__(self, passphrase, salt, iterations=1000,
digestmodule=SHA1, macmodule=HMAC):
self.__macmodule = macmodule
self.__digestmodule = digestmodule
self._setup(passphrase, salt, iterations, self._pseudorandom)
def _pseudorandom(self, key, msg):
"""Pseudorandom function. e.g. HMAC-SHA1"""
return self.__macmodule.new(key=key, msg=msg,
digestmod=self.__digestmodule).digest()
def read(self, bytes):
"""Read the specified number of key bytes."""
if self.closed:
raise ValueError("file-like object is closed")
size = len(self.__buf)
blocks = [self.__buf]
i = self.__blockNum
while size < bytes:
i += 1
if i > 0xffffffffL or i < 1:
# We could return "" here, but
raise OverflowError("derived key too long")
block = self.__f(i)
blocks.append(block)
size += len(block)
buf = "".join(blocks)
retval = buf[:bytes]
self.__buf = buf[bytes:]
self.__blockNum = i
return retval
def __f(self, i):
# i must fit within 32 bits
assert 1 <= i <= 0xffffffffL
U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
result = U
for j in xrange(2, 1+self.__iterations):
U = self.__prf(self.__passphrase, U)
result = strxor(result, U)
return result
def hexread(self, octets):
"""Read the specified number of octets. Return them as hexadecimal.
Note that len(obj.hexread(n)) == 2*n.
"""
return b2a_hex(self.read(octets))
def _setup(self, passphrase, salt, iterations, prf):
# Sanity checks:
# passphrase and salt must be str or unicode (in the latter
# case, we convert to UTF-8)
if isinstance(passphrase, unicode):
passphrase = passphrase.encode("UTF-8")
if not isinstance(passphrase, str):
raise TypeError("passphrase must be str or unicode")
if isinstance(salt, unicode):
salt = salt.encode("UTF-8")
if not isinstance(salt, str):
raise TypeError("salt must be str or unicode")
# iterations must be an integer >= 1
if not isinstance(iterations, (int, long)):
raise TypeError("iterations must be an integer")
if iterations < 1:
raise ValueError("iterations must be at least 1")
# prf must be callable
if not callable(prf):
raise TypeError("prf must be callable")
self.__passphrase = passphrase
self.__salt = salt
self.__iterations = iterations
self.__prf = prf
self.__blockNum = 0
self.__buf = ""
self.closed = False
def close(self):
"""Close the stream."""
if not self.closed:
del self.__passphrase
del self.__salt
del self.__iterations
del self.__prf
del self.__blockNum
del self.__buf
self.closed = True
def crypt(word, salt=None, iterations=None):
"""PBKDF2-based unix crypt(3) replacement.
The number of iterations specified in the salt overrides the 'iterations'
parameter.
The effective hash length is 192 bits.
"""
# Generate a (pseudo-)random salt if the user hasn't provided one.
if salt is None:
salt = _makesalt()
# salt must be a string or the us-ascii subset of unicode
if isinstance(salt, unicode):
salt = salt.encode("us-ascii")
if not isinstance(salt, str):
raise TypeError("salt must be a string")
# word must be a string or unicode (in the latter case, we convert to UTF-8)
if isinstance(word, unicode):
word = word.encode("UTF-8")
if not isinstance(word, str):
raise TypeError("word must be a string or unicode")
# Try to extract the real salt and iteration count from the salt
if salt.startswith("$p5k2$"):
(iterations, salt, dummy) = salt.split("$")[2:5]
if iterations == "":
iterations = 400
else:
converted = int(iterations, 16)
if iterations != "%x" % converted: # lowercase hex, minimum digits
raise ValueError("Invalid salt")
iterations = converted
if not (iterations >= 1):
raise ValueError("Invalid salt")
# Make sure the salt matches the allowed character set
allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
for ch in salt:
if ch not in allowed:
raise ValueError("Illegal character %r in salt" % (ch,))
if iterations is None or iterations == 400:
iterations = 400
salt = "$p5k2$$" + salt
else:
salt = "$p5k2$%x$%s" % (iterations, salt)
rawhash = PBKDF2(word, salt, iterations).read(24)
# return salt + "$" + b64encode(rawhash, "./") DO: Original return line
return salt + "$" + rawhash
# Add crypt as a static method of the PBKDF2 class
# This makes it easier to do "from PBKDF2 import PBKDF2" and still use
# crypt.
PBKDF2.crypt = staticmethod(crypt)
def _makesalt():
"""Return a 48-bit pseudorandom salt for crypt().
This function is not suitable for generating cryptographic secrets.
"""
binarysalt = "".join([pack("@H", randint(0, 0xffff)) for i in range(3)])
return b64encode(binarysalt, "./")
def test_pbkdf2():
"""Module self-test"""
from binascii import a2b_hex
#
# Test vectors from RFC 3962
#
# Test 1
result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16)
expected = a2b_hex("cdedb5281bb2f801565a1122b2563515")
if result != expected:
raise RuntimeError("self-test failed")
# Test 2
result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32)
expected = ("5c08eb61fdf71e4e4ec3cf6ba1f5512b"
"a7e52ddbc5e5142f708a31e2e62b1e13")
if result != expected:
raise RuntimeError("self-test failed")
# Test 3
result = PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32)
expected = ("139c30c0966bc32ba55fdbf212530ac9"
"c5ec59f1a452f5cc9ad940fea0598ed1")
if result != expected:
raise RuntimeError("self-test failed")
# Test 4
result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
expected = ("9ccad6d468770cd51b10e6a68721be61"
"1a8b4d282601db3b36be9246915ec82a")
if result != expected:
raise RuntimeError("self-test failed")
#
# Other test vectors
#
# Chunked read
f = PBKDF2("kickstart", "workbench", 256)
result = f.read(17)
result += f.read(17)
result += f.read(1)
result += f.read(2)
result += f.read(3)
expected = PBKDF2("kickstart", "workbench", 256).read(40)
if result != expected:
raise RuntimeError("self-test failed")
#
# crypt() test vectors
#
# crypt 1
result = crypt("cloadm", "exec")
expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
if result != expected:
raise RuntimeError("self-test failed")
# crypt 2
result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
if result != expected:
raise RuntimeError("self-test failed")
# crypt 3
result = crypt("dcl", "tUsch7fU", iterations=13)
expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
if result != expected:
raise RuntimeError("self-test failed")
# crypt 4 (unicode)
result = crypt(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
'$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
if result != expected:
raise RuntimeError("self-test failed")
print "PBKDF2 self test successfull"
if __name__ == "__main__":
default_key = binascii.a2b_hex("404142434445464748494A4B4C4D4E4F")
host_chal = binascii.a2b_hex("".join("89 45 19 BF BC 1A 5B D8".split()))
card_chal = binascii.a2b_hex("".join("27 4D B7 EA CA 66 CE 44".split()))
card_crypto = binascii.a2b_hex("".join("8A D4 A9 2D 9B 6B 24 E0".split()))
session_key = get_session_key(default_key, host_chal, card_chal)
print "Session-Key: ", utils.hexdump(session_key)
print verify_card_cryptogram(session_key, host_chal, card_chal, card_crypto)
host_crypto = calculate_host_cryptogram(session_key, card_chal, host_chal)
print "Host-Crypto: ", utils.hexdump( host_crypto )
external_authenticate = binascii.a2b_hex("".join("84 82 01 00 10".split())) + host_crypto
print utils.hexdump(calculate_MAC(session_key, external_authenticate, iv))
too_short = binascii.a2b_hex("".join("89 45 19 BF".split()))
padded = append_padding("DES3-ECB",len(too_short),too_short)
print "Padded data: " + utils.hexdump(padded)
unpadded = strip_padding("DES3-ECB",padded)
print "Without padding: " + utils.hexdump(unpadded)
test_pbkdf2()

View File

@@ -0,0 +1,105 @@
import TLVutils
from time import time
from random import seed, randint
from ConstantDefinitions import *
from utils import inttostring, stringtoint
class ControlReferenceTemplate:
def __init__(self,type,config=""):
"""
Generates a new CRT
@param type: Type of the CRT (HT, AT, KT, CCT, DST, CT-sym, CT-asym)
@param config: A string containing TLV encoded Security Environment parameters
"""
if type not in (TEMPLATE_AT, TEMPLATE_HT, TEMPLATE_KAT, TEMPLATE_CCT, TEMPLATE_DST, TEMPLATE_CT):
raise ValueError, "Unknown control reference tag."
else:
self.type = type
self.iv = None
self.keyref = None
self.key = None
self.fileref = None
self.DFref = None
self.keylength = None
self.algorithm = None
self.usage_qualifier = None
if config != "":
self.parse_SE_config(config)
self.__config_string = config
def parse_SE_config(self,config):
structure = TLVutils.unpack(config)
for object in structure:
tag, length, value = object
if tag == 0x80:
self.__set_algo(data)
elif tag in (0x81,0x82,0x83,0x84):
self.__set_key(tag,length,value)
elif tag in range(0x85, 0x93):
self.__set_iv(tag,length,value)
elif tag == 0x95:
self.usage_qualifier = data
def __set_algo(self,data):
if not ALGO_MAPPING.has_key(data):
raise ValueError
else:
#TODO: Sanity checking
self.algorithm = ALGO_MAPPING[data]
self.__replace_tag(0x80,data)
def __set_key(self,tag,length,value):
if tag == 0x81:
self.fileref = value
elif tag == 0x82:
self.DFref = value
elif tag in (0x83,0x84):
#Todo: Resolve reference
self.keyref = value
def __set_iv(self,tag,length,value):
iv = None
if tag == 0x85:
iv = 0x00
elif tag == 0x86:
pass #What is initial chaining block?
elif tag == 0x87 or tag == 0x93:
if length != 0:
iv = value
else:
iv = self.iv + 1
elif tag == 0x91:
if length != 0:
iv = value
else:
seed(time())
iv = hex(randint(0,255))
elif tag == 0x92:
if length != 0:
iv = value
else:
iv = int(time())
self.iv = iv
def __replace_tag(self,tag,data):
position = 0
while self.__config_string[position:position+1] != tag and position < len(self.__config_string):
length = inttostring(self.__config_string[position+1:position+2])
position += length + 3
if position < len(self.__config_string): #Replace Tag
length = inttostring(self.__config_string[position+1:position+2])
self.__config_string = self.__config_string[:position] + tag + inttostring(len(data)) + data + self.__config_string[position+2+length:]
else: #Add new tag
self.__config_string += tag + inttostring(len(data)) + data
def to_string(self):
"""
Return the content of the CRT, encoded as TLV data in a string
"""
return self.__config_string
def __str__(self):
return self.__config_string

View File

@@ -0,0 +1,115 @@
# Meaning of the interindustry values of SW1-SW2 {{{
SW = {
"NORMAL" : 0x9000,
"NORMAL_REST" : 0x6100,
"WARN_NOINFO62" : 0x6200,
"WARN_DATACORRUPTED" : 0x6281,
"WARN_EOFBEFORENEREAD" : 0x6282,
"WARN_DEACTIVATED" : 0x6283,
"WARN_FCIFORMATTING" : 0x6284,
"WARN_TERMINATIONSTATE" : 0x6285,
"WARN_NOINPUTSENSOR" : 0x6286,
"WARN_NOINFO63" : 0x6300,
"WARN_FILEFILLED" : 0x6381,
"ERR_EXECUTION" : 0x6400,
"ERR_RESPONSEREQUIRED" : 0x6401,
"ERR_NOINFO65" : 0x6500,
"ERR_MEMFAILURE" : 0x6581,
"ERR_WRONGLENGTH" : 0x6700,
"ERR_NOINFO68" : 0x6800,
"ERR_CHANNELNOTSUPPORTED" : 0x6881,
"ERR_SECMESSNOTSUPPORTED" : 0x6882,
"ERR_LASTCMDEXPECTED" : 0x6883,
"ERR_CHAININGNOTSUPPORTED" : 0x6884,
"ERR_NOINFO69" : 0x6900,
"ERR_INCOMPATIBLEWITHFILE" : 0x6981,
"ERR_SECSTATUS" : 0x6982,
"ERR_AUTHBLOCKED" : 0x6983,
"ERR_REFNOTUSABLE" : 0x6984,
"ERR_CONDITIONNOTSATISFIED" : 0x6985,
"ERR_NOCURRENTEF" : 0x6986,
"ERR_SECMESSOBJECTSMISSING" : 0x6987,
"ERR_SECMESSOBJECTSINCORRECT" : 0x6988,
"ERR_NOINFO6A" : 0x6A00,
"ERR_INCORRECTPARAMETERS" : 0x6A80,
"ERR_NOTSUPPORTED" : 0x6A81,
"ERR_FILENOTFOUND" : 0x6A82,
"ERR_RECORDNOTFOUND" : 0x6A83,
"ERR_NOTENOUGHMEMORY" : 0x6A84,
"ERR_NCINCONSISTENTWITHTLV" : 0x6A85,
"ERR_INCORRECTP1P2" : 0x6A86,
"ERR_NCINCONSISTENTP1P2" : 0x6A87,
"ERR_DATANOTFOUND" : 0x6A88,
"ERR_FILEEXISTS" : 0x6A89,
"ERR_DFNAMEEXISTS" : 0x6A8A,
"ERR_OFFSETOUTOFFILE" : 0x6B00,
"ERR_INSNOTSUPPORTED" : 0x6D00,
}
SW_MESSAGES = {
0x9000 : 'Normal processing (No further qualification)',
0x6200 : 'Warning processing (State of non-volatile memory is unchanged): No information given',
0x6281 : 'Warning processing (State of non-volatile memory is unchanged): Part of returned data may be corrupted',
0x6282 : 'Warning processing (State of non-volatile memory is unchanged): End of file or record reached before reading Ne bytes',
0x6283 : 'Warning processing (State of non-volatile memory is unchanged): Selected file deactivated',
0x6284 : 'Warning processing (State of non-volatile memory is unchanged): File control information not formatted according to 5.3.3',
0x6285 : 'Warning processing (State of non-volatile memory is unchanged): Selected file in termination state',
0x6286 : 'Warning processing (State of non-volatile memory is unchanged): No input data available from a sensor on the card',
0x6300 : 'Warning processing (State of non-volatile memory has changed): No information given',
0x6381 : 'Warning processing (State of non-volatile memory has changed): File filled up by the last write',
0x6400 : 'Execution error (State of non-volatile memory is unchanged): Execution error',
0x6401 : 'Execution error (State of non-volatile memory is unchanged): Immediate response required by the card',
0x6500 : 'Execution error (State of non-volatile memory has changed): No information given',
0x6581 : 'Execution error (State of non-volatile memory has changed): Memory failure',
0x6700 : 'Checking error: Wrong length; no further indication',
0x6800 : 'Checking error (Functions in CLA not supported): No information given',
0x6881 : 'Checking error (Functions in CLA not supported): Logical channel not supported',
0x6882 : 'Checking error (Functions in CLA not supported): Secure messaging not supported',
0x6883 : 'Checking error (Functions in CLA not supported): Last command of the chain expected',
0x6884 : 'Checking error (Functions in CLA not supported): Command chaining not supported',
0x6900 : 'Checking error (Command not allowed): No information given',
0x6981 : 'Checking error (Command not allowed): Command incompatible with file structure',
0x6982 : 'Checking error (Command not allowed): Security status not satisfied',
0x6983 : 'Checking error (Command not allowed): Authentication method blocked',
0x6984 : 'Checking error (Command not allowed): Reference data not usable',
0x6985 : 'Checking error (Command not allowed): Conditions of use not satisfied',
0x6986 : 'Checking error (Command not allowed): Command not allowed (no current EF)',
0x6987 : 'Checking error (Command not allowed): Expected secure messaging data objects missing',
0x6988 : 'Checking error (Command not allowed): Incorrect secure messaging data objects',
0x6A00 : 'Checking error (Wrong parameters P1-P2): No information given',
0x6A80 : 'Checking error (Wrong parameters P1-P2): Incorrect parameters in the command data field',
0x6A81 : 'Checking error (Wrong parameters P1-P2): Function not supported',
0x6A82 : 'Checking error (Wrong parameters P1-P2): File or application not found',
0x6A83 : 'Checking error (Wrong parameters P1-P2): Record not found',
0x6A84 : 'Checking error (Wrong parameters P1-P2): Not enough memory space in the file',
0x6A85 : 'Checking error (Wrong parameters P1-P2): Nc inconsistent with TLV structure',
0x6A86 : 'Checking error (Wrong parameters P1-P2): Incorrect parameters P1-P2',
0x6A87 : 'Checking error (Wrong parameters P1-P2): Nc inconsistent with parameters P1-P2',
0x6A88 : 'Checking error (Wrong parameters P1-P2): Referenced data or reference data not found (exact meaning depending on the command)',
0x6A89 : 'Checking error (Wrong parameters P1-P2): File already exists',
0x6A8A : 'Checking error (Wrong parameters P1-P2): DF name already exists',
0x6B00 : 'Checking error (Wrong parameters P1-P2): Wrong parameters (offset outside the EF)',
0x6D00 : 'Checking error (Instruction code not supported or invalid)',
0x6E00 : 'Checking error (Class not supported)',
0x6F00 : 'Checking error (No precise diagnosis)',
}
for i in range(0, 0xff):
SW_MESSAGES[0x6100 + i] = 'Normal Processing (%d data bytes still available)' % i
SW_MESSAGES[0x6600 + i] = 'Execution error (Security-related issues)'
SW_MESSAGES[0x6C00 + i] = 'Checking error (Wrong Le field; %d available data bytes)' % i
# }}}
class SwError(Exception):
def __init__(self, sw):
self.sw = sw
self.message = SW_MESSAGES[sw]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,298 @@
TAG = {}
TAG["FILECONTROLPARAMETERS"] = 0x62
TAG["FILEMANAGEMENTDATA"] = 0x64
TAG["FILECONTROLINFORMATION"] = 0x6F
TAG["BYTES_EXCLUDINGSTRUCTURE"] = 0x80
TAG["BYTES_INCLUDINGSTRUCTURE"] = 0x81
TAG["FILEDISCRIPTORBYTE"] = 0x82
TAG["FILEIDENTIFIER"] = 0x83
TAG["DFNAME"] = 0x84
TAG["PROPRIETARY_NOTBERTLV"] = 0x85
TAG["PROPRIETARY_SECURITY"] = 0x86
TAG["FIDEF_CONTAININGFCI"] = 0x87
TAG["SHORTFID"] = 0x88
TAG["LIFECYCLESTATUS"] = 0x8A
TAG["SA_EXPANDEDFORMAT"] = 0x8B
TAG["SA_COMPACTFORMAT"] = 0x8C
TAG["FIDEF_CONTAININGSET"] = 0x8D
TAG["CHANNELSECURITY"] = 0x8E
TAG["SA_DATAOBJECTS"] = 0xA0
TAG["PROPRIETARY_SECURITYTEMP"] = 0xA1
TAG["PROPRIETARY_BERTLV"] = 0xA5
TAG["SA_EXPANDEDFORMAT_TEMP"] = 0xAB
TAG["CRYPTIDENTIFIER_TEMP"] = 0xAC
TAG["DISCRETIONARY_DATA"] = 0x53
TAG["DISCRETIONARY_TEMPLATE"] = 0x73
TAG["OFFSET_DATA"] = 0x54
TAG["TAG_LIST"] = 0x5C
TAG["HEADER_LIST"] = 0x5D
TAG["EXTENDED_HEADER_LIST"] = 0x4D
MAX_EXTENDED_LE = 0xffff
MAX_SHORT_LE = 0xff
def tlv_unpack(data): # {{{
ber_class = (ord(data[0]) & 0xC0) >> 6
constructed = (ord(data[0]) & 0x20) != 0 ## 0 = primitive, 0x20 = constructed
tag = ord(data[0])
data = data[1:]
if (tag & 0x1F) == 0x1F:
tag = (tag << 8) | ord(data[0])
while ord(data[0]) & 0x80 == 0x80:
data = data[1:]
tag = (tag << 8) | ord(data[0])
data = data[1:]
length = ord(data[0])
if length < 0x80:
data = data[1:]
elif length & 0x80 == 0x80:
length_ = 0
data = data[1:]
for i in range(0,length & 0x7F):
length_ = length_ * 256 + ord(data[0])
data = data[1:]
length = length_
value = data[:length]
rest = data[length:]
return ber_class, constructed, tag, length, value, rest
# }}}
def tlv_find_tags(tlv_data, tags, num_results = None): # {{{
"""Find (and return) all instances of tags in the given tlv structure (as
returned by unpack). If num_results is specified then at most that many
results will be returned."""
results = []
def find_recursive(tlv_data):
for d in tlv_data:
t,l,v = d[:3]
if t in tags:
results.append(d)
else:
if isinstance(v, list): # FIXME Refactor the whole TLV code into a class
find_recursive(v)
if num_results is not None and len(results) >= num_results:
return
find_recursive(tlv_data)
return results
# }}}
def tlv_find_tag(tlv_data, tag, num_results = None): # {{{
"""Find (and return) all instances of tag in the given tlv structure (as returned by unpack).
If num_results is specified then at most that many results will be returned."""
return tlv_find_tags(tlv_data, [tag], num_results)
# }}}
def pack(tlv_data, recalculate_length = False): # {{{
result = []
for data in tlv_data:
tag, length, value = data[:3]
if tag in (0xff, 0x00):
result.append( chr(tag) )
continue
if not isinstance(value, str):
value = pack(value, recalculate_length)
if recalculate_length:
length = len(value)
t = ""
while tag > 0:
t = chr( tag & 0xff ) + t
tag = tag >> 8
if length < 0x7F:
l = chr(length)
else:
l = ""
while length > 0:
l = chr( length & 0xff ) + l
length = length >> 8
assert len(l) < 0x7f
l = chr( 0x80 | len(l) ) + l
result.append(t)
result.append(l)
result.append(value)
return "".join(result)
# }}}
def bertlv_pack(data): # {{{
"""Packs a bertlv list of 3-tuples (tag, length, newvalue) into a string"""
return pack(data)
# }}}
def unpack(data, with_marks = None, offset = 0, include_filler=False): # {{{
result = []
while len(data) > 0:
if ord(data[0]) in (0x00, 0xFF):
if include_filler:
if with_marks is None:
result.append( (ord(data[0]), None, None) )
else:
result.append( (ord(data[0]), None, None, () ) )
data = data[1:]
offset = offset + 1
continue
l = len(data)
ber_class, constructed, tag, length, value, data = tlv_unpack(data)
stop = offset + (l - len(data))
start = stop - length
if with_marks is not None:
marks = []
for type, mark_start, mark_stop in with_marks:
if (mark_start, mark_stop) == (start, stop):
marks.append(type)
marks = (marks, )
else:
marks = ()
if not constructed:
result.append( (tag, length, value) + marks )
else:
result.append( (tag, length, unpack(value, with_marks, offset = start)) + marks )
offset = stop
return result
# }}}
def bertlv_unpack(data): # {{{
"""Unpacks a bertlv coded string into a list of 3-tuples (tag, length,
newvalue)."""
return unpack(data)
# }}}
def simpletlv_pack(tlv_data, recalculate_length = False): # {{{
result = ""
for tag, length, value in tlv_data:
if tag >= 0xff or tag <= 0x00:
# invalid
continue
if recalculate_length:
length = len(value)
if length > 0xffff or length < 0:
# invalid
continue
if length < 0xff:
result += chr(tag) + chr(length) + value
else:
result += chr(tag) + chr(0xff)+chr(length>>8)+chr(length&0xff) + value
return result
# }}}
def simpletlv_unpack(data): # {{{
"""Unpacks a simpletlv coded string into a list of 3-tuples (tag, length,
newvalue)."""
result = []
rest = data
while rest != '':
tag = ord(rest[0])
if tag == 0 or tag == 0xff:
raise ValueError
length = ord(rest[1])
if length == 0xff:
length = (ord(rest[2])<<8) + ord(rest[3])
newvalue = rest[4:4+length]
rest = rest[4+length:]
else:
newvalue = rest[2:2+length]
rest = rest[2+length:]
result.append((tag, length, newvalue))
return result
# }}}
def decodeDiscretionaryDataObjects(tlv_data): # {{{
datalist = []
for (tag, length, newvalue) in (tlv_find_tags(tlv_data,
[TAG["DISCRETIONARY_DATA"], TAG["DISCRETIONARY_TEMPLATE"]])):
datalist.append(newvalue)
return datalist
# }}}
def decodeOffsetDataObjects(tlv_data): # {{{
offsets = []
for (tag, length, newvalue) in tlv_find_tag(tlv_data,
TAG["OFFSET_DATA"]):
offsets.append(stringtoint(newvalue))
return offsets
# }}}
def decodeTagList(tlv_data): # {{{
taglist = []
for (t, l, data) in tlv_find_tag(tlv_data, TAG["TAG_LIST"]):
while data != "":
tag = ord(data[0])
data = data[1:]
if (tag & 0x1F) == 0x1F:
tag = (tag << 8) | ord(data[0])
while ord(data[0]) & 0x80 == 0x80:
data = data[1:]
tag = (tag << 8) | ord(data[0])
data = data[1:]
taglist.append((tag, 0))
return taglist
# }}}
def decodeHeaderList(tlv_data): # {{{
headerlist = []
for (t, l, data) in tlv_find_tag(tlv_data, TAG["HEADER_LIST"]):
while data != "":
tag = ord(data[0])
data = data[1:]
if (tag & 0x1F) == 0x1F:
tag = (tag << 8) | ord(data[0])
while ord(data[0]) & 0x80 == 0x80:
data = data[1:]
tag = (tag << 8) | ord(data[0])
data = data[1:]
length = ord(data[0])
if length < 0x80:
data = data[1:]
elif length & 0x80 == 0x80:
length_ = 0
data = data[1:]
for i in range(0,length & 0x7F):
length_ = length_ * 256 + ord(data[0])
data = data[1:]
length = length_
headerlist.append((tag, length))
return headerlist
# }}}
def decodeExtendedHeaderList(tlv_data): # {{{
# TODO
return []
# }}}
def encodebertlvDatalist(tag, datalist): # {{{
tlvlist = []
for data in datalist:
tlvlist.append((tag, len(data), data))
return bertlv_pack(tlvlist)
# }}}
def encodeDiscretionaryDataObjects(datalist): # {{{
return encodebertlvDatalist(TAG["DISCRETIONARY_DATA"], datalist)
# }}}
def encodeDataOffsetObjects(datalist): # {{{
return encodebertlvDatalist(TAG["OFFSET_DATA"], datalist)
# }}}

View File

@@ -0,0 +1,550 @@
from ConstantDefinitions import *
from TLVutils import *
from SWutils import SwError, SW
from SmartcardFilesystem import prettyprint_anything, MF, DF, CryptoflexMF, TransparentStructureEF
from utils import C_APDU, R_APDU, hexdump, stringtoint, inttostring
from SmartcardSAM import CardContainer, SAM, PassportSAM, CryptoflexSAM
from pickle import dumps, loads
import socket, struct, sys, signal, atexit, traceback
class SmartcardOS(object): # {{{
def __init__(self, filename,mf=None,ins2handler=None, maxle=MAX_SHORT_LE,sam=None):
self.config_key = "DUMMYKEYDUMMYKEY" #TODO: Let user enter password
self.filename = filename
self.mf = mf
self.SAM = sam
if self.filename != None:
try:
self.load(filename,self.config_key)
except ValueError:
print "Failed to load configuration %s" % filename
if self.SAM == None:
print "Using default SAM. Insecure!!!"
self.SAM = SAM("testconfig.sam",self.config_key) #FIXME: Replace by defaul_SAM()
if self.mf == None:
print "Using default MF."
self.mf = MF(filedescriptor=FDB["DF"], lifecycle=LCB["ACTIVATED"], dfname=None)
self.SAM.set_MF(self.mf)
# pgp directory
#self.mf.append(DF(parent=self.mf,
#fid=4, dfname='\xd2\x76\x00\x01\x24\x01', bertlv_data=[]))
# pkcs-15 directories
#self.mf.append(DF(parent=self.mf,
#fid=1, dfname='\xa0\x00\x00\x00\x01'))
#self.mf.append(DF(parent=self.mf,
#fid=2, dfname='\xa0\x00\x00\x03\x08\x00\x00\x10\x00'))
#self.mf.append(DF(parent=self.mf,
#fid=3, dfname='\xa0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00'))
#import imp, os.path
#SAM_config = os.path.join(os.path.dirname(imp.find_module("SmartcardSAM")[1]), "testconfig.sam") #Path to initial SAM configuration, stored on disk
if not ins2handler:
self.ins2handler = {
0x0c: self.mf.eraseRecord,
0x0e: self.mf.eraseBinaryPlain,
0x0f: self.mf.eraseBinaryEncapsulated,
0x2a: self.SAM.perform_security_operation,
0x20: self.SAM.verify,
0x22: self.SAM.manage_security_environment,
0x24: self.SAM.change_reference_data,
0x46: self.SAM.generate_public_key_pair,
0x82: self.SAM.external_authenticate,
0x84: self.SAM.get_challenge,
0x88: self.SAM.internal_authenticate,
0xa0: self.mf.searchBinaryPlain,
0xa1: self.mf.searchBinaryEncapsulated,
0xa4: self.mf.selectFile,
0xb0: self.mf.readBinaryPlain,
0xb1: self.mf.readBinaryEncapsulated,
0xb2: self.mf.readRecordPlain,
0xb3: self.mf.readRecordEncapsulated,
0xc0: self.getResponse,
0xca: self.mf.getDataPlain,
0xcb: self.mf.getDataEncapsulated,
0xd0: self.mf.writeBinaryPlain,
0xd1: self.mf.writeBinaryEncapsulated,
0xd2: self.mf.writeRecord,
0xd6: self.mf.updateBinaryPlain,
0xd7: self.mf.updateBinaryEncapsulated,
0xda: self.mf.putDataPlain,
0xdb: self.mf.putDataEncapsulated,
0xdc: self.mf.updateRecordPlain,
0xdd: self.mf.updateRecordEncapsulated,
0xe0: self.mf.createFile,
0xe2: self.mf.appendRecord,
0xe4: self.mf.deleteFile,
}
else:
self.ins2handler = ins2handler
self.maxle = maxle
self.lastCommandOffcut = ""
self.lastCommandSW = SW["NORMAL"]
card_capabilities = self.mf.firstSFT + self.mf.secondSFT + SmartcardOS.makeThirdSoftwareFunctionTable()
self.atr = SmartcardOS.makeATR(T=1, directConvention = True, TA1=0x13,
histChars = chr(0x80) + chr(0x70 + len(card_capabilities)) +
card_capabilities)
def save(self):
"""
Save the configuration of the current Smartcard (MF + SAM) to disk.
To files will be stored: <filename>.mf for the mf and <filename>.sam
for the SAM.
Both files will be encrypted.
"""
if self.filename == None:
raise ValueError, "No filename specified"
else:
mf = dumps(self.mf)
path = self.filename + ".mf"
mf = self.SAM.saveToDisk(mf,path)
path = self.filename + ".sam"
self.SAM.saveConfiguration(path)
#self.SAM.saveConfiguration(path,"DUMMYKEYDUMMYKEY")
def load(self,filename,password):
"""
Try to load a configuration from the filesystem. MF or SAM are only loaded if
they aren't yet specified.
"""
if self.SAM == None:
self.SAM = SAM("testconfig.sam","DUMMYKEYDUMMYKEY",None) #FIXME: replace by default_SAM()
path = filename + ".sam"
try:
self.SAM.loadConfiguration(path,password)
except IOError:
print "Failed to open %s" % path
if self.mf == None:
path = filename + ".mf"
data = self.SAM.loadFromDisk(path,password)
self.mf = loads(data)
print "Succesfully loaded MF from %s" % path
def powerUp(self):
pass
def powerDown(self):
pass
@staticmethod
def makeATR(**args): # {{{
"""Calculate Answer to Reset (ATR) and returns the bitstring.
directConvention -- Bool. Whether to use direct convention or inverse convention.
TAi, TBi, TCi -- (optional) Value between 0 and 0xff. Interface Characters (for meaning see ISO 7816-3). Note that if no transmission protocol is given, it is automatically selected with T=max{j-1|TAj in args OR TBj in args OR TCj in args}.
T -- (optional) Value between 0 and 15. Transmission Protocol. Note that if T is set, TAi/TBi/TCi for i>T are omitted.
histChars -- (optional) Bitstring with 0 <= len(histChars) <= 15. Historical Characters T1 to T15 (for meaning see ISO 7816-4).
T0, TDi and TCK are automatically calculated.
"""
# first byte TS
if args["directConvention"]:
atr = "\x3b"
else:
atr = "\x3f"
if args.has_key("T"):
T = args["T"]
else:
T = 0
# find maximum i of TAi/TBi/TCi in args
maxTD = 0
i = 15
while i > 0:
if args.has_key("TA" + str(i)) or args.has_key("TB" + str(i)) or args.has_key("TC" + str(i)):
maxTD = i-1
break
i -= 1
if maxTD == 0 and T > 0:
maxTD = 2
# insert TDi into args (TD0 is actually T0)
for i in range(0, maxTD+1):
if i == 0 and args.has_key("histChars"):
args["TD0"] = len(args["histChars"])
else:
args["TD"+str(i)] = T
if i < maxTD:
args["TD"+str(i)] |= 1<<7
if args.has_key("TA" + str(i+1)):
args["TD"+str(i)] |= 1<<4
if args.has_key("TB" + str(i+1)):
args["TD"+str(i)] |= 1<<5
if args.has_key("TC" + str(i+1)):
args["TD"+str(i)] |= 1<<6
# initialize checksum
TCK = 0
# add TDi, TAi, TBi and TCi to ATR (TD0 is actually T0)
for i in range(0, maxTD+1):
atr = atr + "%c" % args["TD" + str(i)]
TCK ^= args["TD" + str(i)]
for j in ["A", "B", "C"]:
if args.has_key("T" + j + str(i+1)):
atr += "%c" % args["T" + j + str(i+1)]
# calculate checksum for all bytes from T0 to the end
TCK ^= args["T" + j + str(i+1)]
# add historical characters
if args.has_key("histChars"):
atr += args["histChars"]
for i in range(0, len(args["histChars"])):
TCK ^= ord( args["histChars"][i] )
# checksum is omitted for T=0
if T > 0:
atr += "%c" % TCK
return atr
# }}}
@staticmethod
def makeThirdSoftwareFunctionTable(commandChainging=False,
extendedLe=False, assignLogicalChannel=0, maximumChannels=0): # {{{
"""
Returns a byte according to the third software function table from the
historical bytes of the card capabilities.
"""
tsft = 0
if commandChainging:
tsft |= 1 << 7
if extendedLe:
tsft |= 1 << 6
if assignLogicalChannel:
if not (0<=assignLogicalChannel and assignLogicalChannel<=3):
raise ValueError
tsft |= assignLogicalChannel << 3
if maximumChannels:
if not (0<=maximumChannels and maximumChannels<=7):
raise ValueError
tsft |= maximumChannels
return inttostring(tsft)
# }}}
def formatResult(self, le, data, sw, sm):
if le == None:
count = 0
elif le == 0:
count = self.maxle
else:
count = le
self.lastCommandOffcut = data[count:]
l = len(self.lastCommandOffcut)
if l == 0:
self.lastCommandSW = SW["NORMAL"]
else:
self.lastCommandSW = sw
sw = SW["NORMAL_REST"] + min(0xff, l)
result = data[:count]
if sm:
sw, result = self.SAM.protect_result(sw,result)
return R_APDU(result, inttostring(sw)).render()
def getResponse(self, p1, p2, data):
if not (p1 == 0 and p2 == 0):
raise SwError(SW["ERR_INCORRECTP1P2"])
return self.lastCommandSW, self.lastCommandOffcut
def execute(self, msg):
def notImplemented(*argz, **args):
raise SwError(SW["ERR_INSNOTSUPPORTED"])
c = C_APDU(msg)
#Handle Class Byte{{{
class_byte = c.cla
SM_STATUS = None
logical_channel = 0
command_chaining = 0
header_authentication = 0
#Ugly Hack for OpenSC-explorer
if(class_byte == 0xb0):
print "Open SC APDU"
SM_STATUS = "No SM"
#If Bit 8,7,6 == 0 then first industry values are used
if (class_byte & 0xE0 == 0x00):
#Bit 1 and 2 specify the logical channel
logical_channel = class_byte & 0x03
#Bit 3 and 4 specify secure messaging
secure_messaging = class_byte >> 2
secure_messaging &= 0x03
if (secure_messaging == 0x00):
SM_STATUS = "No SM"
elif (secure_messaging == 0x01):
SM_STATUS = "Propietary SM" # Not supported ?
elif (secure_messaging == 0x02):
SM_STATUS = "Standard SM"
elif (secure_messaging == 0x03):
SM_STATUS = "Standard SM"
header_authentication = 1
#If Bit 8,7 == 01 then further industry values are used
elif (class_byte & 0x0C == 0x0C):
#Bit 1 to 4 specify logical channel. 4 is added, value range is from four to nineteen
logical_channel = class_byte & 0x0f
logical_channel += 4
#Bit 6 indicates secure messaging
secure_messaging = class_byte >> 5
secure_messaging &= 0x01
if (secure_messaging == 0x00):
SM_STATUS = "No SM"
elif (secure_messaging == 0x01):
SM_STATUS = "Standard SM"
#In both cases Bit 5 specifiys command chaining
command_chaining = class_byte >> 5
command_chaining &= 0x01
#}}}
try:
if SM_STATUS == "Standard SM":
c = self.SAM.parse_SM_CAPDU(c,header_authentication)
elif SM_STATUS == "Propietary SM":
raise SwError("ERR_SECMESSNOTSUPPORTED")
sw, result = self.ins2handler.get(c.ins, notImplemented)(c.p1, c.p2, c.data)
if SM_STATUS == "Standard SM":
answer = self.formatResult(c.le, result, sw, True)
else:
answer = self.formatResult(c.le, result, sw, False)
except SwError, e:
print e.message
#traceback.print_exception(*sys.exc_info())
sw = e.sw
result = ""
answer = self.formatResult(c.le, result, sw, False)
return answer
# }}}
class PassportOS(SmartcardOS):
"""
The PassportOS emulates a Passport Application according to ICAO MRTD standard.
It generates a data structure ...
It also integrates a SAM derived from the standard SmartcardSAM, providing the
Basic Access Control (BAC) mechanisms.
"""
def __init__(self, filename,mf=None, ins2handler=None, maxle=MAX_SHORT_LE):
if filename == None and mf == None:
mf = MF()
else:
pass #TODO: Load data from disk
self.generate_data_structure(mf)
self.SAM = PassportSAM(mf)
SmartcardOS.__init__(self, None, mf=mf, ins2handler=ins2handler, maxle=maxle,sam=self.SAM)
def generate_data_structure(self,mf):
#MRZ1 = "P<UTOERIKSSON<<ANNA<MARIX<<<<<<<<<<<<<<<<<<<"
#MRZ2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14"
MRZ1 = "IDD<<OEPEN<<DOMINIK<<<<<<<<<<<<<<<<<"
MRZ2 = "6863089495D<<8312251<0908195<<<<<<<0"
MRZ = MRZ1+MRZ2
from PIL import Image
picturepath = "jp2.jpg"
try:
im = Image.open(picturepath)
pic_width, pic_height = im.size
fd = open(picturepath,"rb")
picture = fd.read()
fd.close()
except IOError:
print "Could not find picture %s" % picturepath
pic_width = 0
pic_height = 0
picture = None
#We need a MF with Application DF \xa0\x00\x00\x02G\x10\x01
mf.append(DF(parent=mf,fid=4, dfname='\xa0\x00\x00\x02G\x10\x01', bertlv_data=[]))
df = mf.currentDF()
mf.append(TransparentStructureEF(parent=df, fid=0x011E, filedescriptor=0, data=""))#EF.COM
mf.append(TransparentStructureEF(parent=df, fid=0x0101, filedescriptor=0, data=""))#EF.DG1
mf.append(TransparentStructureEF(parent=df, fid=0x0102, filedescriptor=0, data=""))#EF.DG2
mf.append(TransparentStructureEF(parent=df, fid=0x010D, filedescriptor=0, data=""))#EF.SOD
#EF.COM
COM = pack([(0x5F01,4,"0107"),(0x5F36,6,"040000"),(0x5C,2,"6175")])
COM = pack(((0x60,len(COM),COM),))
fid = df.select("fid",0x011E)
fid.writebinary([0],[COM])
#EF.DG1
DG1 = pack([(0x5F1F,len(MRZ),MRZ)])
DG1 = pack([(0x61,len(DG1),DG1)])
fid = df.select("fid",0x0101)
fid.writebinary([0],[DG1])
#EF.DG2
if picture != None:
IIB = "\x00\x01" + inttostring(pic_width,2) + inttostring(pic_height,2) + 6 * "\x00"
length = 32 + len(picture) #32 is the length of IIB + FIB
FIB = inttostring(length,4) + 16 * "\x00"
FRH = "FAC" + "\x00" + "010" + "\x00" + inttostring(14+length,4) + inttostring(1,2)
picture = FRH + FIB + IIB + picture
DG2 = pack([(0xA1,8,"\x87\x02\x01\x01\x88\x02\x05\x01"),(0x5F2E,len(picture),picture)])
DG2 = pack([(0x02,1,"\x01"),(0x7F60,len(DG2),DG2)])
DG2 = pack([(0x7F61,len(DG2),DG2)])
fid = df.select("fid",0x0102)
fid.writebinary([0],[DG2])
class CryptoflexOS(SmartcardOS): # {{{
def __init__(self, filename, mf=None, ins2handler=None, maxle=MAX_SHORT_LE):
if filename == None and mf == None:
mf = CryptoflexMF()
#TODO: Load objects from disk
self.firstrun = True
SmartcardOS.__init__(self, None,mf=mf, ins2handler=ins2handler, maxle=maxle,
sam=CryptoflexSAM("testconfig.sam","DUMMYKEYDUMMYKEY"))
card_capabilities = self.mf.firstSFT + self.mf.secondSFT + SmartcardOS.makeThirdSoftwareFunctionTable()
SmartcardOS.atr = SmartcardOS.makeATR(T=0, directConvention = True, TA1=0x13,
histChars = chr(0x80) + chr(0x70 + len(card_capabilities)) +
card_capabilities)
def powerUp(self):
self.firstrun = True
def execute(self, msg):
def notImplemented(*argz, **args):
raise SwError(SW["ERR_INSNOTSUPPORTED"])
c = C_APDU(msg)
try:
sw, result = self.ins2handler.get(c.ins, notImplemented)(c.p1, c.p2, c.data)
#print type(result)
except SwError, e:
print e.message
#traceback.print_exception(*sys.exc_info())
sw = e.sw
result = ""
r = self.formatResult(c.ins, c.le, result, sw)
return r
def formatResult(self, ins, le, data, sw):
if le == 0 and len(data):
# cryptoflex does not inpterpret le==0 as maxle
self.lastCommandSW = sw
self.lastCommandOffcut = data
return inttostring(SW["ERR_WRONGLENGTH"]|min(0xff,len(data)))
else:
r = SmartcardOS.formatResult(self, le, data, sw, False)
if self.firstrun:
# FIXME: other than Cryptoflex Access TM Software Development Kit
# Release 3C, smartcard_login-0.1.1 requires:
self.firstrun = False
if ord(r[0]) == SW["NORMAL_REST"]>>8:
r = inttostring(SW["NORMAL"])
# be fully successfull if at least one databyte has been returned
if len(r) > 2 and r[-2] == chr(SW["NORMAL_REST"]>>8):
return r[:-2] + inttostring(SW["NORMAL"])
return r
# }}}
class VirtualICC(object): # {{{
def __init__(self, filename,os=None, lenlen=3, host="localhost", port=35963):
if os == None:
self.os = SmartcardOS(filename)
else:
self.os = os
self.filename = filename
self.sock = self.connectToPort(host, port)
self.sock.settimeout(None)
self.lenlen = lenlen
signal.signal(signal.SIGINT, self.signalHandler)
atexit.register(self.signalHandler)
atexit.register(self.saveCard)
def saveCard(self):
if self.filename != None:
print "Saving smartcard configuration to %s" % self.filename
self.os.save()
else:
print "No filename specified, card configuration will not be saved."
def signalHandler(self, signal=None, frame=None):
self.sock.close()
sys.exit()
@staticmethod
def connectToPort(host, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
return sock
def __sendToVPICC(self, msg):
size = inttostring(len(msg), self.lenlen)
self.sock.sendall("%s%s" % (size, msg))
def __recvFromVPICC(self):
# receive message size
size = self.sock.recv(self.lenlen)
size = stringtoint(size)
# receive and return message
if size == 0:
return ""
else:
msg = self.sock.recv(size)
#print("Incoming Message % s") % hexdump(msg) #TEST
return msg
#return self.sock.recv(size) TEST, wieder auskommentieren!
def run(self):
fd_in = open("input","w") #TEST
fd_out = open("output","w") #TEST
while True :
msg = self.__recvFromVPICC()
if msg == "":
self.__sendToVPICC(self.os.atr)
elif msg == chr(0):
# powerdown
print "Power Down"
self.os.powerDown()
elif msg == chr(1):
# powerup
print "Power Up"
self.os.powerUp()
else:
print "APDU (%d Bytes):\n%s" % (len(msg),hexdump(msg, short=True))
apdu = struct.pack("i" + str(len(msg)) + "s",len(msg),msg) #TEST
fd_in.write(apdu) #TEST
answer = self.os.execute(msg)
print "RESP (%d Bytes):\n%s\n" % (len(answer),hexdump(answer, short=True))
rapdu = struct.pack("!i" + str(len(answer)) + "s",len(answer),answer) #TEST
fd_out.write(rapdu) #TEST
self.__sendToVPICC(answer)
# }}}
if __name__ == "__main__":
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-t", "--type", action="store", type="choice",
default='iso7816',
choices=['iso7816', 'cryptoflex', 'ePass'],
help="Type of Smartcard [default: %default]")
parser.add_option("-f", "--file", action="store", type="string",
dest="filename", default=None,
help="Name of a smartcard stored in the filesystem. The card will be loaded")
(options, args) = parser.parse_args()
if options.type == 'cryptoflex':
vicc = VirtualICC(options.filename,os=CryptoflexOS(options.filename))
elif options.type == 'ePass':
#raise ValueError, "Not implemented yet!"
vicc = VirtualICC(options.filename,os=PassportOS(options.filename))
else:
vicc = VirtualICC(options.filename)
vicc.run()

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1 @@
$p5k2$$RGHiUkhA$<24>U<EFBFBD>x<01>U<EFBFBD><55><EFBFBD><EFBFBD><EFBFBD>3I^낎P(<28>f<EFBFBD>Kc<4B><63>iveY/0<><30><EFBFBD><EFBFBD>l<EFBFBD><6C>h<EFBFBD><05>{?<0E>gy<67><79><EFBFBD><EFBFBD>a<EFBFBD><61><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>P<><50><EFBFBD><EFBFBD><12>?Z<><1F>U!<21><>o<EFBFBD><6F>KΦP<CEA6>,<2C><><EFBFBD><EFBFBD><63>o<EFBFBD>{0F<30>\<5C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>g#<23><>~<7E><><72>l<EFBFBD>/}<7D>l<EFBFBD><6C>3<19><><EFBFBD>#<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>%<06>ѱ@<40>OG

View File

@@ -0,0 +1,2 @@
$p5k2$$kj3EFei1$F<>M?<3F><><EFBFBD>?06ՋD<D58B>
# 6$<24>2vz<76><7A>t<EFBFBD>(<28>\rCiKs<4B><73><EFBFBD>:<3A>8<EFBFBD>D<03><>DF<44>poR<6F><52>+<<3C>Y2-<2D>kF<6B><46>$<24><><EFBFBD>C<EFBFBD>C߯<><16>'<27><>#,<2C>

View File

@@ -0,0 +1,575 @@
import string, binascii, sys, re, getopt
def stringtoint(str): # {{{
#i = len(str) - 1
#int = 0
#while i >= 0:
#int = (int << i*8) + ord(str[i])
#i = i - 1
#return int
if str:
return int(str.encode('hex'), 16)
return 0
# }}}
def inttostring(i, length=None): # {{{
#str = ""
#while i > 0:
#str = chr(i & 0xff) + str
#i >>= 8
str = "%x" % i
if len(str) % 2 == 0:
str = str.decode('hex')
else:
str = ("0"+str).decode('hex')
if length:
l = len(str)
if l > length:
raise ValueError, "i too big for the specified stringlength"
else:
str = chr(0)*(length-l) + str
return str
# }}}
def represent_binary_fancy(len, value, mask = 0):
result = []
for i in range(len):
if i%4 == 0:
result.append( " " )
if i%8 == 0:
result.append( " " )
if mask & 0x01:
result.append( str(value & 0x01) )
else:
result.append( "." )
mask = mask >> 1
value = value >> 1
result.reverse()
return "".join(result).strip()
def parse_binary(value, bytemasks, verbose = False, value_len = 8):
## Parses a binary structure and gives information back
## bytemasks is a sequence of (mask, value, string_if_no_match, string_if_match) tuples
result = []
for mask, byte, nonmatch, match in bytemasks:
if verbose:
prefix = represent_binary_fancy(value_len, value, mask) + ": "
else:
prefix = ""
if (value & mask) == (byte & mask):
if match is not None:
result.append(prefix + match)
else:
if nonmatch is not None:
result.append(prefix + nonmatch)
return result
_myprintable = " " + string.letters + string.digits + string.punctuation
def hexdump(data, indent = 0, short = False, linelen = 16, offset = 0):
"""Generates a nice hexdump of data and returns it. Consecutive lines will
be indented with indent spaces. When short is true, will instead generate
hexdump without adresses and on one line.
Examples:
hexdump('\x00\x41') -> \
'0000: 00 41 .A '
hexdump('\x00\x41', short=True) -> '00 41 (.A)'"""
def hexable(data):
return " ".join([binascii.b2a_hex(a) for a in data])
def printable(data):
return "".join([e in _myprintable and e or "." for e in data])
if short:
return "%s (%s)" % (hexable(data), printable(data))
FORMATSTRING = "%04x: %-"+ str(linelen*3) +"s %-"+ str(linelen) +"s"
result = ""
(head, tail) = (data[:linelen], data[linelen:])
pos = 0
while len(head) > 0:
if pos > 0:
result = result + "\n%s" % (' ' * indent)
result = result + FORMATSTRING % (pos+offset, hexable(head), printable(head))
pos = pos + len(head)
(head, tail) = (tail[:linelen], tail[linelen:])
return result
LIFE_CYCLES = {0x01: "Load file = loaded",
0x03: "Applet instance / security domain = Installed",
0x07: "Card manager = Initialized; Applet instance / security domain = Selectable",
0x0F: "Card manager = Secured; Applet instance / security domain = Personalized",
0x7F: "Card manager = Locked; Applet instance / security domain = Blocked",
0xFF: "Applet instance = Locked"}
def parse_status(data):
"""Parses the Response APDU of a GetStatus command."""
def parse_segment(segment):
def parse_privileges(privileges):
if privileges == 0x0:
return "N/A"
else:
privs = []
if privileges & (1<<7):
privs.append("security domain")
if privileges & (1<<6):
privs.append("DAP DES verification")
if privileges & (1<<5):
privs.append("delegated management")
if privileges & (1<<4):
privs.append("card locking")
if privileges & (1<<3):
privs.append("card termination")
if privileges & (1<<2):
privs.append("default selected")
if privileges & (1<<1):
privs.append("global PIN modification")
if privileges & (1<<0):
privs.append("mandated DAP verification")
return ", ".join(privs)
lgth = ord(segment[0])
aid = segment[1:1+lgth]
lifecycle = ord(segment[1+lgth])
privileges = ord(segment[1+lgth+1])
print "aid length: %i (%x)" % (lgth, lgth)
print "aid: %s" % hexdump(aid, indent = 18, short=True)
print "life cycle state: %x (%s)" % (lifecycle, LIFE_CYCLES.get(lifecycle, "unknown or invalid state"))
print "privileges: %x (%s)\n" % (privileges, parse_privileges(privileges))
pos = 0
while pos < len(data):
lgth = ord(data[pos])+3
segment = data[pos:pos+lgth]
parse_segment(segment)
pos = pos + lgth
def _unformat_hexdump(dump):
hexdump = " ".join([line[7:54] for line in dump.splitlines()])
return binascii.a2b_hex("".join([e != " " and e or "" for e in hexdump]))
def _make_byte_property(prop):
"Make a byte property(). This is meta code."
return property(lambda self: getattr(self, "_"+prop, None),
lambda self, value: self._setbyte(prop, value),
lambda self: delattr(self, "_"+prop),
"The %s attribute of the APDU" % prop)
class APDU(object):
"Base class for an APDU"
def __init__(self, *args, **kwargs):
"""Creates a new APDU instance. Can be given positional parameters which
must be sequences of either strings (or strings themselves) or integers
specifying byte values that will be concatenated in order. Alternatively
you may give exactly one positional argument that is an APDU instance.
After all the positional arguments have been concatenated they must
form a valid APDU!
The keyword arguments can then be used to override those values.
Keywords recognized are:
C_APDU: cla, ins, p1, p2, lc, le, data
R_APDU: sw, sw1, sw2, data
"""
initbuff = list()
if len(args) == 1 and isinstance(args[0], self.__class__):
self.parse( args[0].render() )
else:
for arg in args:
if type(arg) == str:
initbuff.extend(arg)
elif hasattr(arg, "__iter__"):
for elem in arg:
if hasattr(elem, "__iter__"):
initbuff.extend(elem)
else:
initbuff.append(elem)
else:
initbuff.append(arg)
for (index, value) in enumerate(initbuff):
t = type(value)
if t == str:
initbuff[index] = ord(value)
elif t != int:
raise TypeError, "APDU must consist of ints or one-byte strings, not %s (index %s)" % (t, index)
self.parse( initbuff )
for (name, value) in kwargs.items():
if value is not None:
setattr(self, name, value)
def _getdata(self):
return self._data
def _setdata(self, value):
if isinstance(value, str):
self._data = "".join([e for e in value])
elif isinstance(value, list):
self._data = "".join([chr(int(e)) for e in value])
else:
raise ValueError, "'data' attribute can only be a str or a list of int, not %s" % type(value)
self.Lc = len(value)
def _deldata(self):
del self._data; self.data = ""
data = property(_getdata, _setdata, None,
"The data contents of this APDU")
def _setbyte(self, name, value):
#print "setbyte(%r, %r)" % (name, value)
if isinstance(value, int):
setattr(self, "_"+name, value)
elif isinstance(value, str):
setattr(self, "_"+name, ord(value))
else:
raise ValueError, "'%s' attribute can only be a byte, that is: int or str, not %s" % (namelower, type(value))
def _format_parts(self, fields):
"utility function to be used in __str__ and __repr__"
parts = []
for i in fields:
parts.append( "%s=0x%02X" % (i, getattr(self, i)) )
return parts
def __str__(self):
result = "%s(%s)" % (self.__class__.__name__, ", ".join(self._format_fields()))
if len(self.data) > 0:
result = result + " with %i (0x%02x) bytes of data" % (
len(self.data), len(self.data)
)
return result + ":\n" + hexdump(self.data)
else:
return result
def __repr__(self):
parts = self._format_fields()
if len(self.data) > 0:
parts.append("data=%r" % self.data)
return "%s(%s)" % (self.__class__.__name__, ", ".join(parts))
class C_APDU(APDU):
"Class for a command APDU"
def parse(self, apdu):
"Parse a full command APDU and assign the values to our object, overwriting whatever there was."
apdu = map( lambda a: (isinstance(a, str) and (ord(a),) or (a,))[0], apdu)
apdu = apdu + [0] * max(4-len(apdu), 0)
self.CLA, self.INS, self.P1, self.P2 = apdu[:4] # case 1, 2, 3, 4
if len(apdu) == 5: # case 2
self.Le = apdu[-1]
self.data = ""
elif len(apdu) > 5: # case 3, 4
self.Lc = apdu[4]
if len(apdu) == 5 + self.Lc: # case 3
self.data = apdu[5:]
elif len(apdu) == 5 + self.Lc + 1: # case 4
self.data = apdu[5:-1]
self.Le = apdu[-1]
else:
raise ValueError, "Invalid Lc value. Is %s, should be %s or %s" % (self.Lc,
5 + self.Lc, 5 + self.Lc + 1)
else: # case 1
self.data = ""
CLA = _make_byte_property("CLA"); cla = CLA
INS = _make_byte_property("INS"); ins = INS
P1 = _make_byte_property("P1"); p1 = P1
P2 = _make_byte_property("P2"); p2 = P2
Lc = _make_byte_property("Lc"); lc = Lc
Le = _make_byte_property("Le"); le = Le
def _format_fields(self):
fields = ["CLA", "INS", "P1", "P2"]
if self.Lc > 0:
fields.append("Lc")
if hasattr(self, "_Le"): ## There's a difference between "Le = 0" and "no Le"
fields.append("Le")
return self._format_parts(fields)
def render(self):
"Return this APDU as a binary string"
buffer = []
for i in self.CLA, self.INS, self.P1, self.P2:
buffer.append(chr(i))
if len(self.data) > 0:
buffer.append(chr(self.Lc))
buffer.append(self.data)
if hasattr(self, "_Le"):
buffer.append(chr(self.Le))
return "".join(buffer)
def case(self):
"Return 1, 2, 3 or 4, depending on which ISO case we represent."
if self.Lc == 0:
if not hasattr(self, "_Le"):
return 1
else:
return 2
else:
if not hasattr(self, "_Le"):
return 3
else:
return 4
_apduregex = re.compile(r'^\s*([0-9a-f]{2}\s*){4,}$', re.I)
_fancyapduregex = re.compile(r'^\s*([0-9a-f]{2}\s*){4,}\s*((xx|yy)\s*)?(([0-9a-f]{2}|:|\)|\(|\[|\])\s*)*$', re.I)
@staticmethod
def parse_fancy_apdu(*args):
apdu_string = " ".join(args)
if not C_APDU._fancyapduregex.match(apdu_string):
raise ValueError
apdu_string = apdu_string.lower()
have_le = False
pos = apdu_string.find("xx")
if pos == -1:
pos = apdu_string.find("yy")
have_le = True
apdu_head = ""
apdu_tail = apdu_string
if pos != -1:
apdu_head = apdu_string[:pos]
apdu_tail = apdu_string[pos+2:]
if apdu_head.strip() != "" and not C_APDU._apduregex.match(apdu_head):
raise ValueError
class Node(list):
def __init__(self, parent = None, type = None):
list.__init__(self)
self.parent = parent
self.type = type
def make_binary(self):
"Recursively transform hex strings to binary"
for index, child in enumerate(self):
if isinstance(child,str):
child = "".join( ("".join(child.split())).split(":") )
assert len(child) % 2 == 0
self[index] = binascii.a2b_hex(child)
else:
child.make_binary()
def calculate_lengths(self):
"Recursively calculate lengths and insert length counts"
self.length = 0
index = 0
while index < len(self): ## Can't use enumerate() due to the insert() below
child = self[index]
if isinstance(child,str):
self.length = self.length + len(child)
else:
child.calculate_lengths()
formatted_len = binascii.a2b_hex("%02x" % child.length) ## FIXME len > 255?
self.length = self.length + len(formatted_len) + child.length
self.insert(index, formatted_len)
index = index + 1
index = index + 1
def flatten(self, offset = 0, ignore_types=["("]):
"Recursively flatten, gather list of marks"
string_result = []
mark_result = []
for child in self:
if isinstance(child,str):
string_result.append(child)
offset = offset + len(child)
else:
start = offset
child_string, child_mark = child.flatten(offset, ignore_types)
string_result.append(child_string)
offset = end = offset + len(child_string)
if not child.type in ignore_types:
mark_result.append( (child.type, start, end) )
mark_result.extend(child_mark)
return "".join(string_result), mark_result
tree = Node()
current = tree
allowed_parens = {"(": ")", "[":"]"}
for pos,char in enumerate(apdu_tail):
if char in (" ", "a", "b", "c", "d", "e", "f",":") or char.isdigit():
if len(current) > 0 and isinstance(current[-1],str):
current[-1] = current[-1] + char
else:
current.append(str(char))
elif char in allowed_parens.values():
if current.parent is None:
raise ValueError
if allowed_parens[current.type] != char:
raise ValueError
current = current.parent
elif char in allowed_parens.keys():
current.append( Node(current, char) )
current = current[-1]
else:
raise ValueError
if current != tree:
raise ValueError
tree.make_binary()
tree.calculate_lengths()
apdu_head = apdu_head.strip()
if apdu_head != "":
l = tree.length
if have_le:
l = l - 1 ## FIXME Le > 255?
formatted_len = "%02x" % l ## FIXME len > 255?
apdu_head = binascii.a2b_hex("".join( (apdu_head + formatted_len).split() ))
apdu_tail, marks = tree.flatten(offset=0)
apdu = C_APDU(apdu_head + apdu_tail, marks = marks)
return apdu
class R_APDU(APDU):
"Class for a response APDU"
def _getsw(self): return chr(self.SW1) + chr(self.SW2)
def _setsw(self, value):
if len(value) != 2:
raise ValueError, "SW must be exactly two bytes"
self.SW1 = value[0]
self.SW2 = value[1]
SW = property(_getsw, _setsw, None,
"The Status Word of this response APDU")
sw = SW
SW1 = _make_byte_property("SW1"); sw1 = SW1
SW2 = _make_byte_property("SW2"); sw2 = SW2
def parse(self, apdu):
"Parse a full response APDU and assign the values to our object, overwriting whatever there was."
self.SW = apdu[-2:]
self.data = apdu[:-2]
def _format_fields(self):
fields = ["SW1", "SW2"]
return self._format_parts(fields)
def render(self):
"Return this APDU as a binary string"
return self.data + self.sw
if __name__ == "__main__":
response = """
0000: 07 A0 00 00 00 03 00 00 07 00 07 A0 00 00 00 62 ...............b
0010: 00 01 01 00 07 A0 00 00 00 62 01 01 01 00 07 A0 .........b......
0020: 00 00 00 62 01 02 01 00 07 A0 00 00 00 62 02 01 ...b.........b..
0030: 01 00 07 A0 00 00 00 03 00 00 01 00 0E A0 00 00 ................
0040: 00 30 00 00 90 07 81 32 10 00 00 01 00 0E A0 00 .0.....2........
0050: 00 00 30 00 00 90 07 81 42 10 00 00 01 00 0E A0 ..0.....B.......
0060: 00 00 00 30 00 00 90 07 81 41 10 00 00 07 00 0E ...0.....A......
0070: A0 00 00 00 30 00 00 90 07 81 12 10 00 00 01 00 ....0...........
0080: 09 53 4C 42 43 52 59 50 54 4F 07 00 90 00 .SLBCRYPTO....
""" # 64kv1 vorher
response = """
0000: 07 A0 00 00 00 03 00 00 0F 00 07 A0 00 00 00 62 ...............b
0010: 00 01 01 00 07 A0 00 00 00 62 01 01 01 00 07 A0 .........b......
0020: 00 00 00 62 01 02 01 00 07 A0 00 00 00 62 02 01 ...b.........b..
0030: 01 00 07 A0 00 00 00 03 00 00 01 00 08 A0 00 00 ................
0040: 00 30 00 CA 10 01 00 0E A0 00 00 00 30 00 00 90 .0..........0...
0050: 07 81 32 10 00 00 01 00 0E A0 00 00 00 30 00 00 ..2..........0..
0060: 90 07 81 42 10 00 00 01 00 0E A0 00 00 00 30 00 ...B..........0.
0070: 00 90 07 81 41 10 00 00 07 00 0E A0 00 00 00 30 ....A..........0
0080: 00 00 90 07 81 12 10 00 00 01 00 09 53 4C 42 43 ............SLBC
0090: 52 59 50 54 4F 07 00 90 00 RYPTO....
""" # komische Karte
response = """
0000: 07 A0 00 00 00 03 00 00 07 00 07 A0 00 00 00 62 ...............b
0010: 00 01 01 00 07 A0 00 00 00 62 01 01 01 00 07 A0 .........b......
0020: 00 00 00 62 01 02 01 00 07 A0 00 00 00 62 02 01 ...b.........b..
0030: 01 00 07 A0 00 00 00 03 00 00 01 00 0E A0 00 00 ................
0040: 00 30 00 00 90 07 81 32 10 00 00 01 00 0E A0 00 .0.....2........
0050: 00 00 30 00 00 90 07 81 42 10 00 00 01 00 0E A0 ..0.....B.......
0060: 00 00 00 30 00 00 90 07 81 41 10 00 00 07 00 0E ...0.....A......
0070: A0 00 00 00 30 00 00 90 07 81 12 10 00 00 01 00 ....0...........
0080: 09 53 4C 42 43 52 59 50 54 4F 07 00 05 A0 00 00 .SLBCRYPTO......
0090: 00 01 01 00 90 00 ......
""" # 64kv1 nachher
response = """
0000: 07 A0 00 00 00 03 00 00 07 00 07 A0 00 00 00 62 ...............b
0010: 00 01 01 00 07 A0 00 00 00 62 01 01 01 00 07 A0 .........b......
0020: 00 00 00 62 01 02 01 00 07 A0 00 00 00 62 02 01 ...b.........b..
0030: 01 00 07 A0 00 00 00 03 00 00 01 00 0E A0 00 00 ................
0040: 00 30 00 00 90 07 81 32 10 00 00 01 00 0E A0 00 .0.....2........
0050: 00 00 30 00 00 90 07 81 42 10 00 00 01 00 0E A0 ..0.....B.......
0060: 00 00 00 30 00 00 90 07 81 41 10 00 00 07 00 0E ...0.....A......
0070: A0 00 00 00 30 00 00 90 07 81 12 10 00 00 01 00 ....0...........
0080: 09 53 4C 42 43 52 59 50 54 4F 07 00 05 A0 00 00 .SLBCRYPTO......
0090: 00 01 01 00 06 A0 00 00 00 01 01 07 02 90 00 ...............
""" # 64k1 nach setup
#response = sys.stdin.read()
#parse_status(_unformat_hexdump(response)[:-2])
a = C_APDU(1,2,3,4) # case 1
b = C_APDU(1,2,3,4,5) # case 2
c = C_APDU((1,2,3), cla=0x23, data="hallo") # case 3
d = C_APDU(1,2,3,4,2,4,6,0) # case 4
print
print a
print b
print c
print d
print
print repr(a)
print repr(b)
print repr(c)
print repr(d)
print
for i in a, b, c, d:
print hexdump(i.render())
print
e = R_APDU(0x90,0)
f = R_APDU("foo\x67\x00")
print
print e
print f
print
print repr(e)
print repr(f)
print
for i in e, f:
print hexdump(i.render())

81
vscarchitecture.bb Normal file
View File

@@ -0,0 +1,81 @@
DESCRIPTION = "Virtual Smartcard with PCSC Driver and CCID to PCSC Gadget"
LICENSE = "GPL"
DEPENDS = "pcsc-lite linux-libc-headers"
RDEPENDS = "pcsc-lite kernel-module-gadgetfs python-pycrypto python-crypt python-textutils python-imaging python-pickle"
SRC_URI = "svn://vsmartcard.svn.sourceforge.net/svnroot/;module=vsmartcard;proto=https;rev=1"
S = "${WORKDIR}"
serialdropdir = ${libdir}/pcsc/drivers/serial
readerconfdir = ${sysconfdir}/reader.conf.d
python_sitelib = /usr/lib/python2.5/site-packages
LIBPCSCLITE_CFLAGS = -I${STAGING_INCDIR}/PCSC -lpcsclite
PTHREAD_CFLAGS = -pthread
# FIXME: let openmoko set the right STAGING_KERNEL_DIR
STAGING_KERNEL_DIR = ${STAGING_DIR}/${MACHINE}-angstrom-${TARGET_OS}/kernel
GADGETFS_CFLAGS = -I${STAGING_KERNEL_DIR}/include
SO_CFLAGS = -fPIC
SO_LDFLAGS = -shared
INSTALL = install
INSTALL_PROGRAM = ${INSTALL}
INSTALL_DATA = ${INSTALL} -m 644
FILES_${PN} = "\
${bindir}/* \
${readerconfdir}/* \
${serialdropdir}/* \
${python_sitelib}/* \
"
do_compile() {
${CC} ${S}/NFC/ccid/ccid.c ${S}/NFC/ccid/usbstring.c -o ccid \
${LIBPCSCLITE_CFLAGS} ${GADGETFS_CFLAGS} ${PTHREAD_CFLAGS} ${CFLAGS}
${CC} ${S}/NFC/picc_to_pcsc/picc_to_pcsc.c -o picc_to_pcsc \
${LIBPCSCLITE_CFLAGS} ${CFLAGS}
${CC} -c ${S}/NFC/virtualsmartcard/vpcd/vpcd.c \
${S}/NFC/virtualsmartcard/vpcd/vpcd.h \
${LIBPCSCLITE_CFLAGS} ${SO_CFLAGS} ${CFLAGS}
${CC} vpcd.o -o libvpcd.so \
${LIBPCSCLITE_CFLAGS} ${SO_LDFLAGS} ${LDFLAGS}
rm -f vpcd.o
echo '#!/bin/sh' > virtualsmartcard
echo "cd \"${python_sitelib}\"" \
>> virtualsmartcard
echo "python \"${python_sitelib}/VirtualSmartcard.py\" \"\$@\"" \
>> virtualsmartcard
echo 'FRIENDLYNAME "Virtual PCD"' > vpcd.conf
echo 'DEVICENAME /dev/null' >> vpcd.conf
echo "LIBPATH ${serialdropdir}/libvpcd.so" >> vpcd.conf
echo 'CHANNELID 0' >> vpcd.conf
}
do_install() {
${INSTALL} -d ${D}${bindir}
${INSTALL} -d ${D}${serialdropdir}
${INSTALL} -d ${D}${sysconfdir}/reader.conf.d/
${INSTALL} -d ${D}${python_sitelib}
${INSTALL_PROGRAM} ccid ${D}${bindir}
${INSTALL_PROGRAM} picc_to_pcsc ${D}${bindir}
${INSTALL_PROGRAM} virtualsmartcard ${D}${bindir}
${INSTALL_PROGRAM} libvpcd.so ${D}${serialdropdir}
${INSTALL_DATA} vpcd.conf ${D}${readerconfdir}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/ConstantDefinitions.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/CryptoUtils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/SEutils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/SmartcardFilesystem.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/SmartcardSAM.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/SWutils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/TLVutils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/utils.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/VirtualSmartcard.py ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/testconfig.sam ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/testconfig.mf ${D}${python_sitelib}
${INSTALL_DATA} ${S}/NFC/virtualsmartcard/vpicc/jp2.jpg ${D}${python_sitelib}
}