From: hackbard Date: Thu, 19 Jan 2006 23:21:46 +0000 (+0000) Subject: initial checkin of harald welte's original librfid project X-Git-Url: https://hackdaworld.org/gitweb/?p=rfid%2Flibrfid.git;a=commitdiff_plain;h=6bddaf273b8eb8ececc1d4cc73dcce717c604a10 initial checkin of harald welte's original librfid project --- 6bddaf273b8eb8ececc1d4cc73dcce717c604a10 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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 Library General +Public License instead of this License. diff --git a/LICENSING b/LICENSING new file mode 100644 index 0000000..0ebe6bb --- /dev/null +++ b/LICENSING @@ -0,0 +1,11 @@ +librfid is licensed under GNU General Public License, Version 2 as published +by the Free Software Foundation (see the file COPYING). + +This means that as soon as you wish to distribute a program that links to +librfid, that other program has to be licensed under the GPL or a compatible +license, too. + +However, an alternative (royalty-based) licensing is possible. If you want to +create a proprieatary program based on librfid, contact Harald Welte + for licensing details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..69140e2 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,8 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +SUBDIRS = include src utils +LINKOPTS = -lusb + +$(OBJECTS): libtool +libtool: $(LIBTOOL_DEPS) + $(SHELL) ./config.status --recheck diff --git a/README b/README new file mode 100644 index 0000000..c67a48a --- /dev/null +++ b/README @@ -0,0 +1,52 @@ +librfid - low-level RFID access library +(C) 2005 by Harald Welte +====================================================================== + +This library intends to provide a reader- and (as much as possible) +PICC / tag independent API for RFID applications. + +1. Supported Prodocols: + +At this early stage of implementaition, it offers only ISO 14443-2, ISO 14443-3 +A and B as well as ISO 14443-4 (T=CL) support. Other protocols, both open +(such as ISO 15693) and proprietary are to be added as soon as I find some more +time. + +2. Supported Readers: + +At this time only the Omnikey Cardman 5121 reader is supported. + +The cm5121 is a relatively stupid piece of hardware. Basically a contact-based +cm3121 that was enhanced by putting a Philips CL RC632 reader ASIC next to it. +There is no RFID protocol implementation on the cm5121, everything is done on +the host (PC) software. Four primitives (read/write byte/fifo) are provided +via simple PC_to_RDR_Escape CCID messages. + +This makes it the ideal device to learn and play with RFID, since you don't +have any (proprietary) software interfere and puts you in full control of +everything. + +Support for more devices shouldn't be too difficult to add, provided the +devices are stupid enough. More sophisticated readers like Integrated +Engineering or Philips Pegoda do much of the protocol handling in firmware on a +Microcontroller. This makes them (at least till now) faster, but also of +limited use, especially in education and research. + +So if you happen to run into any other dumb RFID readers, especially those +based on Philips CL RC531 and RC632, adding support should be very +straightforward. If you want to contract me for implementing a driver backend, +don't hesitate to contact me. + +3. Installation + +In order to run librfid, you need to provide a PC_to_RDR_Escape function to it. +This is the low-level transport function for communicating. Usually that function would point to a CCID device driver. + +My svn repository at +https://svn.gnumonks.org/trunk/omnikey_cardman/new/userspace/ has an openct +fork with a modified CCID driver. It allows you to use the contact based part +via PC/SC, CT-API, and the contactless part via librfid simultaneously. + + +-- Harald Welte + diff --git a/TODO b/TODO new file mode 100644 index 0000000..136a254 --- /dev/null +++ b/TODO @@ -0,0 +1,30 @@ +rc632: +- fix handling of timeout (program timer of RC632) + +cm5121: +- fix handling of TX or RX > 0x7f [buffer length in atmel chip?] + +iso14443a: +[none] + +iso14443b: +- implement 'option 2' frame markers +- test anticollission (need multiple tags) + +iso15693: +- implement anticollision +- implement all the rest + +mifare_clasic: +- figure out why authentication doensn't work, even though it is exactly like in other drivers + +tcl: +- implement pps for asymmetric (rx/tx) speeds +- test pps + +openct: +- add ifdhandler driver + +other: +- implementation of code for various passive tags +- documentation diff --git a/VERY_IMPORTANT_NOTE b/VERY_IMPORTANT_NOTE new file mode 100644 index 0000000..b1c293a --- /dev/null +++ b/VERY_IMPORTANT_NOTE @@ -0,0 +1,9 @@ +very important note! +-------------------- + +author: hackbard@hackdaworld.org + +librfid (openmrtd.org) is the project of 'Harald Welte' (laforge@gnumonks.org). i just created that repository as i want to add support for the gemini2k rfid +devices and it is much more easier to do development via cvs. + +hackbard diff --git a/autogen.sh b/autogen.sh new file mode 100644 index 0000000..ccce4f0 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +run () +{ + echo "running: $*" + eval $* + + if test $? != 0 ; then + echo "error: while running '$*'" + exit 1 + fi +} + +run aclocal +#run autoheader +run libtoolize -f +run automake -a +run autoconf diff --git a/ccid/ccid-driver.c b/ccid/ccid-driver.c new file mode 100644 index 0000000..fce6d0d --- /dev/null +++ b/ccid/ccid-driver.c @@ -0,0 +1,2448 @@ +/* ccid-driver.c - USB ChipCardInterfaceDevices driver + * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + * Written by Werner Koch. + * + * This file is part of GnuPG. + * + * GnuPG 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 2 of the License, or + * (at your option) any later version. + * + * GnuPG 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU General Public License. If you wish to + * allow use of your version of this file only under the terms of the + * GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS 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. + * + * $Date: 2006-01-19 23:21:46 $ + */ + + +/* CCID (ChipCardInterfaceDevices) is a specification for accessing + smartcard via a reader connected to the USB. + + This is a limited driver allowing to use some CCID drivers directly + without any other specila drivers. This is a fallback driver to be + used when nothing else works or the system should be kept minimal + for security reasons. It makes use of the libusb library to gain + portable access to USB. + + This driver has been tested with the SCM SCR335 and SPR532 + smartcard readers and requires that a reader implements the TPDU + level exchange and does fully automatic initialization. +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if defined(HAVE_LIBUSB) || defined(TEST) + +#include +#include +#include +#include +#include + +#include + +#include "ccid-driver.h" + +#define DRVNAME "ccid-driver: " + + +/* Depending on how this source is used we either define our error + output to go to stderr or to the jnlib based logging functions. We + use the latter when GNUPG_MAJOR_VERSION is defines or when both, + GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined. +*/ +#if defined(GNUPG_MAJOR_VERSION) \ + || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING)) + +#if defined(GNUPG_SCD_MAIN_HEADER) +# include GNUPG_SCD_MAIN_HEADER +#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */ +# include "options.h" +# include "util.h" +# include "memory.h" +# include "cardglue.h" +# else /* This is the modularized GnuPG 1.9 or later. */ +# include "scdaemon.h" +#endif + + +# define DEBUGOUT(t) do { if (debug_level) \ + log_debug (DRVNAME t); } while (0) +# define DEBUGOUT_1(t,a) do { if (debug_level) \ + log_debug (DRVNAME t,(a)); } while (0) +# define DEBUGOUT_2(t,a,b) do { if (debug_level) \ + log_debug (DRVNAME t,(a),(b)); } while (0) +# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \ + log_debug (DRVNAME t,(a),(b),(c));} while (0) +# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ + log_debug (DRVNAME t,(a),(b),(c),(d));} while (0) +# define DEBUGOUT_CONT(t) do { if (debug_level) \ + log_printf (t); } while (0) +# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \ + log_printf (t,(a)); } while (0) +# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \ + log_printf (t,(a),(b)); } while (0) +# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \ + log_printf (t,(a),(b),(c)); } while (0) +# define DEBUGOUT_LF() do { if (debug_level) \ + log_printf ("\n"); } while (0) + +#else /* Other usage of this source - don't use gnupg specifics. */ + +# define DEBUGOUT(t) do { if (debug_level) \ + fprintf (stderr, DRVNAME t); } while (0) +# define DEBUGOUT_1(t,a) do { if (debug_level) \ + fprintf (stderr, DRVNAME t, (a)); } while (0) +# define DEBUGOUT_2(t,a,b) do { if (debug_level) \ + fprintf (stderr, DRVNAME t, (a), (b)); } while (0) +# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \ + fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0) +# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ + fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} while(0) +# define DEBUGOUT_CONT(t) do { if (debug_level) \ + fprintf (stderr, t); } while (0) +# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \ + fprintf (stderr, t, (a)); } while (0) +# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \ + fprintf (stderr, t, (a), (b)); } while (0) +# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \ + fprintf (stderr, t, (a), (b), (c)); } while (0) +# define DEBUGOUT_LF() do { if (debug_level) \ + putc ('\n', stderr); } while (0) + +#endif /* This source not used by scdaemon. */ + + + +enum { + RDR_to_PC_NotifySlotChange= 0x50, + RDR_to_PC_HardwareError = 0x51, + + PC_to_RDR_SetParameters = 0x61, + PC_to_RDR_IccPowerOn = 0x62, + PC_to_RDR_IccPowerOff = 0x63, + PC_to_RDR_GetSlotStatus = 0x65, + PC_to_RDR_Secure = 0x69, + PC_to_RDR_T0APDU = 0x6a, + PC_to_RDR_Escape = 0x6b, + PC_to_RDR_GetParameters = 0x6c, + PC_to_RDR_ResetParameters = 0x6d, + PC_to_RDR_IccClock = 0x6e, + PC_to_RDR_XfrBlock = 0x6f, + PC_to_RDR_Mechanical = 0x71, + PC_to_RDR_Abort = 0x72, + PC_to_RDR_SetDataRate = 0x73, + + RDR_to_PC_DataBlock = 0x80, + RDR_to_PC_SlotStatus = 0x81, + RDR_to_PC_Parameters = 0x82, + RDR_to_PC_Escape = 0x83, + RDR_to_PC_DataRate = 0x84 +}; + + +/* Two macro to detect whether a CCID command has failed and to get + the error code. These macros assume that we can access the + mandatory first 10 bytes of a CCID message in BUF. */ +#define CCID_COMMAND_FAILED(buf) ((buf)[7] & 0x40) +#define CCID_ERROR_CODE(buf) (((unsigned char *)(buf))[8]) + + +/* We need to know the vendor to do some hacks. */ +enum { + VENDOR_SCM = 0x04e6, + VENDOR_CHERRY = 0x046a, + VENDOR_OMNIKEY= 0x076b, + VENDOR_GEMPC = 0x08e6 +}; + + +/* Store information on the driver's state. A pointer to such a + structure is used as handle for most functions. */ +struct ccid_driver_s +{ + usb_dev_handle *idev; + char *rid; + unsigned short id_vendor; + unsigned short id_product; + unsigned short bcd_device; + int ifc_no; + int ep_bulk_out; + int ep_bulk_in; + int ep_intr; + int seqno; + unsigned char t1_ns; + unsigned char t1_nr; + int nonnull_nad; + int auto_ifsd; + int max_ifsd; + int ifsd; + int powered_off; + int has_pinpad; + int apdu_level; /* Reader supports short APDU level exchange. */ +}; + + +static int initialized_usb; /* Tracks whether USB has been initialized. */ +static int debug_level; /* Flag to control the debug output. + 0 = No debugging + 1 = USB I/O info + 2 = T=1 protocol tracing + */ + + +static unsigned int compute_edc (const unsigned char *data, size_t datalen, + int use_crc); +static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen); +static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, + size_t *nread, int expected_type, int seqno, int timeout, + int no_debug); + +/* Convert a little endian stored 4 byte value into an unsigned + integer. */ +static unsigned int +convert_le_u32 (const unsigned char *buf) +{ + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + +static void +set_msg_len (unsigned char *msg, unsigned int length) +{ + msg[1] = length; + msg[2] = length >> 8; + msg[3] = length >> 16; + msg[4] = length >> 24; +} + + +/* Pint an error message for a failed CCID command including a textual + error code. MSG is shall be the CCID message of at least 10 bytes. */ +static void +print_command_failed (const unsigned char *msg) +{ + const char *t; + char buffer[100]; + int ec; + + if (!debug_level) + return; + + ec = CCID_ERROR_CODE (msg); + switch (ec) + { + case 0x00: t = "Command not supported"; break; + + case 0xE0: t = "Slot busy"; break; + case 0xEF: t = "PIN cancelled"; break; + case 0xF0: t = "PIN timeout"; break; + + case 0xF2: t = "Automatic sequence ongoing"; break; + case 0xF3: t = "Deactivated Protocol"; break; + case 0xF4: t = "Procedure byte conflict"; break; + case 0xF5: t = "ICC class not supported"; break; + case 0xF6: t = "ICC protocol not supported"; break; + case 0xF7: t = "Bad checksum in ATR"; break; + case 0xF8: t = "Bad TS in ATR"; break; + + case 0xFB: t = "An all inclusive hardware error occurred"; break; + case 0xFC: t = "Overrun error while talking to the ICC"; break; + case 0xFD: t = "Parity error while talking to the ICC"; break; + case 0xFE: t = "CCID timed out while talking to the ICC"; break; + case 0xFF: t = "Host aborted the current activity"; break; + + default: + if (ec > 0 && ec < 128) + sprintf (buffer, "Parameter error at offset %d", ec); + else + sprintf (buffer, "Error code %02X", ec); + t = buffer; + break; + } + DEBUGOUT_1 ("CCID command failed: %s\n", t); +} + + + + +/* Parse a CCID descriptor, optionally print all available features + and test whether this reader is usable by this driver. Returns 0 + if it is usable. + + Note, that this code is based on the one in lsusb.c of the + usb-utils package, I wrote on 2003-09-01. -wk. */ +static int +parse_ccid_descriptor (ccid_driver_t handle, + const unsigned char *buf, size_t buflen) +{ + unsigned int i; + unsigned int us; + int have_t1 = 0, have_tpdu=0, have_auto_conf = 0; + + + handle->nonnull_nad = 0; + handle->auto_ifsd = 0; + handle->max_ifsd = 32; + handle->ifsd = 0; + handle->has_pinpad = 0; + handle->apdu_level = 0; + DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n", + handle->id_vendor, handle->id_product, handle->bcd_device); + if (buflen < 54 || buf[0] < 54) + { + DEBUGOUT ("CCID device descriptor is too short\n"); + return -1; + } + + DEBUGOUT ("ChipCard Interface Descriptor:\n"); + DEBUGOUT_1 (" bLength %5u\n", buf[0]); + DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]); + DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]); + if (buf[3] != 1 || buf[2] != 0) + DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)"); + DEBUGOUT_LF (); + + DEBUGOUT_1 (" nMaxSlotIndex %5u\n", buf[4]); + DEBUGOUT_2 (" bVoltageSupport %5u %s\n", + buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V" + : buf[5] == 3? "1.8V":"?")); + + us = convert_le_u32 (buf+6); + DEBUGOUT_1 (" dwProtocols %5u ", us); + if ((us & 1)) + DEBUGOUT_CONT (" T=0"); + if ((us & 2)) + { + DEBUGOUT_CONT (" T=1"); + have_t1 = 1; + } + if ((us & ~3)) + DEBUGOUT_CONT (" (Invalid values detected)"); + DEBUGOUT_LF (); + + us = convert_le_u32(buf+10); + DEBUGOUT_1 (" dwDefaultClock %5u\n", us); + us = convert_le_u32(buf+14); + DEBUGOUT_1 (" dwMaxiumumClock %5u\n", us); + DEBUGOUT_1 (" bNumClockSupported %5u\n", buf[18]); + us = convert_le_u32(buf+19); + DEBUGOUT_1 (" dwDataRate %7u bps\n", us); + us = convert_le_u32(buf+23); + DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us); + DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]); + + us = convert_le_u32(buf+28); + DEBUGOUT_1 (" dwMaxIFSD %5u\n", us); + handle->max_ifsd = us; + + us = convert_le_u32(buf+32); + DEBUGOUT_1 (" dwSyncProtocols %08X ", us); + if ((us&1)) + DEBUGOUT_CONT ( " 2-wire"); + if ((us&2)) + DEBUGOUT_CONT ( " 3-wire"); + if ((us&4)) + DEBUGOUT_CONT ( " I2C"); + DEBUGOUT_LF (); + + us = convert_le_u32(buf+36); + DEBUGOUT_1 (" dwMechanical %08X ", us); + if ((us & 1)) + DEBUGOUT_CONT (" accept"); + if ((us & 2)) + DEBUGOUT_CONT (" eject"); + if ((us & 4)) + DEBUGOUT_CONT (" capture"); + if ((us & 8)) + DEBUGOUT_CONT (" lock"); + DEBUGOUT_LF (); + + us = convert_le_u32(buf+40); + DEBUGOUT_1 (" dwFeatures %08X\n", us); + if ((us & 0x0002)) + { + DEBUGOUT (" Auto configuration based on ATR\n"); + have_auto_conf = 1; + } + if ((us & 0x0004)) + DEBUGOUT (" Auto activation on insert\n"); + if ((us & 0x0008)) + DEBUGOUT (" Auto voltage selection\n"); + if ((us & 0x0010)) + DEBUGOUT (" Auto clock change\n"); + if ((us & 0x0020)) + DEBUGOUT (" Auto baud rate change\n"); + if ((us & 0x0040)) + DEBUGOUT (" Auto parameter negotation made by CCID\n"); + else if ((us & 0x0080)) + DEBUGOUT (" Auto PPS made by CCID\n"); + else if ((us & (0x0040 | 0x0080))) + DEBUGOUT (" WARNING: conflicting negotation features\n"); + + if ((us & 0x0100)) + DEBUGOUT (" CCID can set ICC in clock stop mode\n"); + if ((us & 0x0200)) + { + DEBUGOUT (" NAD value other than 0x00 accepted\n"); + handle->nonnull_nad = 1; + } + if ((us & 0x0400)) + { + DEBUGOUT (" Auto IFSD exchange\n"); + handle->auto_ifsd = 1; + } + + if ((us & 0x00010000)) + { + DEBUGOUT (" TPDU level exchange\n"); + have_tpdu = 1; + } + else if ((us & 0x00020000)) + { + DEBUGOUT (" Short APDU level exchange\n"); + handle->apdu_level = 1; + } + else if ((us & 0x00040000)) + { + DEBUGOUT (" Short and extended APDU level exchange\n"); + handle->apdu_level = 1; + } + else if ((us & 0x00070000)) + DEBUGOUT (" WARNING: conflicting exchange levels\n"); + + us = convert_le_u32(buf+44); + DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us); + + DEBUGOUT ( " bClassGetResponse "); + if (buf[48] == 0xff) + DEBUGOUT_CONT ("echo\n"); + else + DEBUGOUT_CONT_1 (" %02X\n", buf[48]); + + DEBUGOUT ( " bClassEnvelope "); + if (buf[49] == 0xff) + DEBUGOUT_CONT ("echo\n"); + else + DEBUGOUT_CONT_1 (" %02X\n", buf[48]); + + DEBUGOUT ( " wlcdLayout "); + if (!buf[50] && !buf[51]) + DEBUGOUT_CONT ("none\n"); + else + DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]); + + DEBUGOUT_1 (" bPINSupport %5u ", buf[52]); + if ((buf[52] & 1)) + { + DEBUGOUT_CONT ( " verification"); + handle->has_pinpad |= 1; + } + if ((buf[52] & 2)) + { + DEBUGOUT_CONT ( " modification"); + handle->has_pinpad |= 2; + } + DEBUGOUT_LF (); + + DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]); + + if (buf[0] > 54) { + DEBUGOUT (" junk "); + for (i=54; i < buf[0]-54; i++) + DEBUGOUT_CONT_1 (" %02X", buf[i]); + DEBUGOUT_LF (); + } + + if (!have_t1 || !(have_tpdu || handle->apdu_level) || !have_auto_conf) + { + DEBUGOUT ("this drivers requires that the reader supports T=1, " + "TPDU or APDU level exchange and auto configuration - " + "this is not available\n"); + return -1; + } + + + /* SCM drivers get stuck in their internal USB stack if they try to + send a frame of n*wMaxPacketSize back to us. Given that + wMaxPacketSize is 64 for these readers we set the IFSD to a value + lower than that: + 64 - 10 CCID header - 4 T1frame - 2 reserved = 48 + Product Ids: + 0xe001 - SCR 331 + 0x5111 - SCR 331-DI + 0x5115 - SCR 335 + 0xe003 - SPR 532 + */ + if (handle->id_vendor == VENDOR_SCM + && handle->max_ifsd > 48 + && ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516) + ||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620) + ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0514) + ||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504) + )) + { + DEBUGOUT ("enabling workaround for buggy SCM readers\n"); + handle->max_ifsd = 48; + } + + + return 0; +} + + +static char * +get_escaped_usb_string (usb_dev_handle *idev, int idx, + const char *prefix, const char *suffix) +{ + int rc; + unsigned char buf[280]; + unsigned char *s; + unsigned int langid; + size_t i, n, len; + char *result; + + if (!idx) + return NULL; + + /* Fixme: The next line is for the current Valgrid without support + for USB IOCTLs. */ + memset (buf, 0, sizeof buf); + + /* First get the list of supported languages and use the first one. + If we do don't find it we try to use English. Note that this is + all in a 2 bute Unicode encoding using little endian. */ + rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + (USB_DT_STRING << 8), 0, + (char*)buf, sizeof buf, 1000 /* ms timeout */); + if (rc < 4) + langid = 0x0409; /* English. */ + else + langid = (buf[3] << 8) | buf[2]; + + rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + (USB_DT_STRING << 8) + idx, langid, + (char*)buf, sizeof buf, 1000 /* ms timeout */); + if (rc < 2 || buf[1] != USB_DT_STRING) + return NULL; /* Error or not a string. */ + len = buf[0]; + if (len > rc) + return NULL; /* Larger than our buffer. */ + + for (s=buf+2, i=2, n=0; i+1 < len; i += 2, s += 2) + { + if (s[1]) + n++; /* High byte set. */ + else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':') + n += 3 ; + else + n++; + } + + result = malloc (strlen (prefix) + n + strlen (suffix) + 1); + if (!result) + return NULL; + + strcpy (result, prefix); + n = strlen (prefix); + for (s=buf+2, i=2; i+1 < len; i += 2, s += 2) + { + if (s[1]) + result[n++] = '\xff'; /* High byte set. */ + else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':') + { + sprintf (result+n, "%%%02X", *s); + n += 3; + } + else + result[n++] = *s; + } + strcpy (result+n, suffix); + + return result; +} + +/* This function creates an reader id to be used to find the same + physical reader after a reset. It returns an allocated and possibly + percent escaped string or NULL if not enough memory is available. */ +static char * +make_reader_id (usb_dev_handle *idev, + unsigned int vendor, unsigned int product, + unsigned char serialno_index) +{ + char *rid; + char prefix[20]; + + sprintf (prefix, "%04X:%04X:", (vendor & 0xfff), (product & 0xffff)); + rid = get_escaped_usb_string (idev, serialno_index, prefix, ":0"); + if (!rid) + { + rid = malloc (strlen (prefix) + 3 + 1); + if (!rid) + return NULL; + strcpy (rid, prefix); + strcat (rid, "X:0"); + } + return rid; +} + + +/* Helper to find the endpoint from an interface descriptor. */ +static int +find_endpoint (struct usb_interface_descriptor *ifcdesc, int mode) +{ + int no; + int want_bulk_in = 0; + + if (mode == 1) + want_bulk_in = 0x80; + for (no=0; no < ifcdesc->bNumEndpoints; no++) + { + struct usb_endpoint_descriptor *ep = ifcdesc->endpoint + no; + if (ep->bDescriptorType != USB_DT_ENDPOINT) + ; + else if (mode == 2 + && ((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) + == USB_ENDPOINT_TYPE_INTERRUPT) + && (ep->bEndpointAddress & 0x80)) + return (ep->bEndpointAddress & 0x0f); + else if (((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) + == USB_ENDPOINT_TYPE_BULK) + && (ep->bEndpointAddress & 0x80) == want_bulk_in) + return (ep->bEndpointAddress & 0x0f); + } + /* Should never happen. */ + return mode == 2? 0x83 : mode == 1? 0x82 :1; +} + + + +/* Combination function to either scan all CCID devices or to find and + open one specific device. + + With READERNO = -1 and READERID is NULL, scan mode is used and + R_RID should be the address where to store the list of reader_ids + we found. If on return this list is empty, no CCID device has been + found; otherwise it points to an allocated linked list of reader + IDs. Note that in this mode the function always returns NULL. + + With READERNO >= 0 or READERID is not NULL find mode is used. This + uses the same algorithm as the scan mode but stops and returns at + the entry number READERNO and return the handle for the the opened + USB device. If R_ID is not NULL it will receive the reader ID of + that device. If R_DEV is not NULL it will the device pointer of + that device. If IFCDESC_EXTRA is NOT NULL it will receive a + malloced copy of the interfaces "extra: data filed; + IFCDESC_EXTRA_LEN receive the lengtyh of this field. If there is + no reader with number READERNO or that reader is not usable by our + implementation NULL will be returned. The caller must close a + returned USB device handle and free (if not passed as NULL) the + returned reader ID info as well as the IFCDESC_EXTRA. On error + NULL will get stored at R_RID, R_DEV, IFCDESC_EXTRA and + IFCDESC_EXTRA_LEN. With READERID being -1 the function stops if + the READERID was found. + + Note that the first entry of the returned reader ID list in scan mode + corresponds with a READERNO of 0 in find mode. +*/ +static usb_dev_handle * +scan_or_find_devices (int readerno, const char *readerid, + char **r_rid, + struct usb_device **r_dev, + unsigned char **ifcdesc_extra, + size_t *ifcdesc_extra_len, + int *interface_number, + int *ep_bulk_out, int *ep_bulk_in, int *ep_intr) +{ + char *rid_list = NULL; + int count = 0; + struct usb_bus *busses, *bus; + struct usb_device *dev = NULL; + usb_dev_handle *idev = NULL; + int scan_mode = (readerno == -1 && !readerid); + + /* Set return values to a default. */ + if (r_rid) + *r_rid = NULL; + if (r_dev) + *r_dev = NULL; + if (ifcdesc_extra) + *ifcdesc_extra = NULL; + if (ifcdesc_extra_len) + *ifcdesc_extra_len = 0; + if (interface_number) + *interface_number = 0; + + /* See whether we want scan or find mode. */ + if (scan_mode) + { + assert (r_rid); + } + + usb_find_busses(); + usb_find_devices(); + +#ifdef HAVE_USB_GET_BUSSES + busses = usb_get_busses(); +#else + busses = usb_busses; +#endif + + for (bus = busses; bus; bus = bus->next) + { + for (dev = bus->devices; dev; dev = dev->next) + { + int cfg_no; + + for (cfg_no=0; cfg_no < dev->descriptor.bNumConfigurations; cfg_no++) + { + struct usb_config_descriptor *config = dev->config + cfg_no; + int ifc_no; + + if(!config) + continue; + + for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++) + { + struct usb_interface *interface + = config->interface + ifc_no; + int set_no; + + if (!interface) + continue; + + for (set_no=0; set_no < interface->num_altsetting; set_no++) + { + struct usb_interface_descriptor *ifcdesc + = interface->altsetting + set_no; + char *rid; + + /* The second condition is for some SCM Micro + SPR 532 which does not know about the + assigned CCID class. Instead of trying to + interpret the strings we simply look at the + product ID. */ + if (ifcdesc && ifcdesc->extra + && ( (ifcdesc->bInterfaceClass == 11 + && ifcdesc->bInterfaceSubClass == 0 + && ifcdesc->bInterfaceProtocol == 0) + || (ifcdesc->bInterfaceClass == 255 + && dev->descriptor.idVendor == 0x04e6 + && dev->descriptor.idProduct == 0xe003))) + { + idev = usb_open (dev); + if (!idev) + { + DEBUGOUT_1 ("usb_open failed: %s\n", + strerror (errno)); + continue; + } + + rid = make_reader_id (idev, + dev->descriptor.idVendor, + dev->descriptor.idProduct, + dev->descriptor.iSerialNumber); + if (rid) + { + if (scan_mode) + { + char *p; + + /* We are collecting infos about all + available CCID readers. Store + them and continue. */ + DEBUGOUT_2 ("found CCID reader %d " + "(ID=%s)\n", + count, rid ); + if ((p = malloc ((rid_list? + strlen (rid_list):0) + + 1 + strlen (rid) + + 1))) + { + *p = 0; + if (rid_list) + { + strcat (p, rid_list); + free (rid_list); + } + strcat (p, rid); + strcat (p, "\n"); + rid_list = p; + } + else /* Out of memory. */ + free (rid); + rid = NULL; + count++; + } + else if (!readerno + || (readerno < 0 + && readerid + && !strcmp (readerid, rid))) + { + /* We found the requested reader. */ + if (ifcdesc_extra && ifcdesc_extra_len) + { + *ifcdesc_extra = malloc (ifcdesc + ->extralen); + if (!*ifcdesc_extra) + { + usb_close (idev); + free (rid); + return NULL; /* Out of core. */ + } + memcpy (*ifcdesc_extra, ifcdesc->extra, + ifcdesc->extralen); + *ifcdesc_extra_len = ifcdesc->extralen; + } + if (interface_number) + *interface_number = (ifcdesc-> + bInterfaceNumber); + if (ep_bulk_out) + *ep_bulk_out = find_endpoint (ifcdesc, 0); + if (ep_bulk_in) + *ep_bulk_in = find_endpoint (ifcdesc, 1); + if (ep_intr) + *ep_intr = find_endpoint (ifcdesc, 2); + + + if (r_dev) + *r_dev = dev; + if (r_rid) + { + *r_rid = rid; + rid = NULL; + } + else + free (rid); + return idev; /* READY. */ + } + else + { + /* This is not yet the reader we + want. fixme: We could avoid the + extra usb_open in this case. */ + if (readerno >= 0) + readerno--; + } + free (rid); + } + + usb_close (idev); + idev = NULL; + goto next_device; + } + } + } + } + next_device: + ; + } + } + + if (scan_mode) + *r_rid = rid_list; + + return NULL; +} + + +/* Set the level of debugging to to usea dn return the old level. -1 + just returns the old level. A level of 0 disables debugging, 1 + enables debugging, 2 enables additional tracing of the T=1 + protocol, other values are not yet defined. */ +int +ccid_set_debug_level (int level) +{ + int old = debug_level; + if (level != -1) + debug_level = level; + return old; +} + + +char * +ccid_get_reader_list (void) +{ + char *reader_list; + + if (!initialized_usb) + { + usb_init (); + initialized_usb = 1; + } + + scan_or_find_devices (-1, NULL, &reader_list, NULL, NULL, NULL, NULL, + NULL, NULL, NULL); + return reader_list; +} + + +/* Open the reader with the internal number READERNO and return a + pointer to be used as handle in HANDLE. Returns 0 on success. */ +int +ccid_open_reader (ccid_driver_t *handle, const char *readerid) +{ + int rc = 0; + struct usb_device *dev = NULL; + usb_dev_handle *idev = NULL; + char *rid = NULL; + unsigned char *ifcdesc_extra = NULL; + size_t ifcdesc_extra_len; + int readerno; + int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; + + *handle = NULL; + + if (!initialized_usb) + { + usb_init (); + initialized_usb = 1; + } + + /* See whether we want to use the reader ID string or a reader + number. A readerno of -1 indicates that the reader ID string is + to be used. */ + if (readerid && strchr (readerid, ':')) + readerno = -1; /* We want to use the readerid. */ + else if (readerid) + { + readerno = atoi (readerid); + if (readerno < 0) + { + DEBUGOUT ("no CCID readers found\n"); + rc = CCID_DRIVER_ERR_NO_READER; + goto leave; + } + } + else + readerno = 0; /* Default. */ + + idev = scan_or_find_devices (readerno, readerid, &rid, &dev, + &ifcdesc_extra, &ifcdesc_extra_len, + &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr); + if (!idev) + { + if (readerno == -1) + DEBUGOUT_1 ("no CCID reader with ID %s\n", readerid ); + else + DEBUGOUT_1 ("no CCID reader with number %d\n", readerno ); + rc = CCID_DRIVER_ERR_NO_READER; + goto leave; + } + + /* Okay, this is a CCID reader. */ + *handle = calloc (1, sizeof **handle); + if (!*handle) + { + DEBUGOUT ("out of memory\n"); + rc = CCID_DRIVER_ERR_OUT_OF_CORE; + goto leave; + } + (*handle)->idev = idev; + (*handle)->rid = rid; + (*handle)->id_vendor = dev->descriptor.idVendor; + (*handle)->id_product = dev->descriptor.idProduct; + (*handle)->bcd_device = dev->descriptor.bcdDevice; + (*handle)->ifc_no = ifc_no; + (*handle)->ep_bulk_out = ep_bulk_out; + (*handle)->ep_bulk_in = ep_bulk_in; + (*handle)->ep_intr = ep_intr; + + DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid ); + + + if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len)) + { + DEBUGOUT ("device not supported\n"); + rc = CCID_DRIVER_ERR_NO_READER; + goto leave; + } + + rc = usb_claim_interface (idev, ifc_no); + if (rc) + { + DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); + rc = CCID_DRIVER_ERR_CARD_IO_ERROR; + goto leave; + } + + leave: + free (ifcdesc_extra); + if (rc) + { + free (rid); + if (idev) + usb_close (idev); + free (*handle); + *handle = NULL; + } + + return rc; +} + + +static void +do_close_reader (ccid_driver_t handle) +{ + int rc; + unsigned char msg[100]; + size_t msglen; + unsigned char seqno; + + if (!handle->powered_off) + { + msg[0] = PC_to_RDR_IccPowerOff; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; /* RFU */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, 0); + msglen = 10; + + rc = bulk_out (handle, msg, msglen); + if (!rc) + bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, + seqno, 2000, 0); + handle->powered_off = 1; + } + if (handle->idev) + { + usb_release_interface (handle->idev, handle->ifc_no); + usb_close (handle->idev); + handle->idev = NULL; + } +} + + +/* Reset a reader on HANDLE. This is useful in case a reader has been + plugged of and inserted at a different port. By resetting the + handle, the same reader will be get used. Note, that on error the + handle won't get released. + + This does not return an ATR, so ccid_get_atr should be called right + after this one. +*/ +int +ccid_shutdown_reader (ccid_driver_t handle) +{ + int rc = 0; + struct usb_device *dev = NULL; + usb_dev_handle *idev = NULL; + unsigned char *ifcdesc_extra = NULL; + size_t ifcdesc_extra_len; + int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; + + if (!handle || !handle->rid) + return CCID_DRIVER_ERR_INV_VALUE; + + do_close_reader (handle); + + idev = scan_or_find_devices (-1, handle->rid, NULL, &dev, + &ifcdesc_extra, &ifcdesc_extra_len, + &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr); + if (!idev) + { + DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid); + return CCID_DRIVER_ERR_NO_READER; + } + + + handle->idev = idev; + handle->ifc_no = ifc_no; + handle->ep_bulk_out = ep_bulk_out; + handle->ep_bulk_in = ep_bulk_in; + handle->ep_intr = ep_intr; + + if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len)) + { + DEBUGOUT ("device not supported\n"); + rc = CCID_DRIVER_ERR_NO_READER; + goto leave; + } + + rc = usb_claim_interface (idev, ifc_no); + if (rc) + { + DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); + rc = CCID_DRIVER_ERR_CARD_IO_ERROR; + goto leave; + } + + leave: + free (ifcdesc_extra); + if (rc) + { + usb_close (handle->idev); + handle->idev = NULL; + } + + return rc; + +} + + +/* Close the reader HANDLE. */ +int +ccid_close_reader (ccid_driver_t handle) +{ + if (!handle || !handle->idev) + return 0; + + do_close_reader (handle); + free (handle->rid); + free (handle); + return 0; +} + + +/* Return False if a card is present and powered. */ +int +ccid_check_card_presence (ccid_driver_t handle) +{ + + return -1; +} + + +/* Write a MSG of length MSGLEN to the designated bulk out endpoint. + Returns 0 on success. */ +static int +bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen) +{ + int rc; + + rc = usb_bulk_write (handle->idev, + handle->ep_bulk_out, + (char*)msg, msglen, + 1000 /* ms timeout */); + if (rc == msglen) + return 0; + + if (rc == -1) + DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno)); + else + DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc); + return CCID_DRIVER_ERR_CARD_IO_ERROR; +} + + +/* Read a maximum of LENGTH bytes from the bulk in endpoint into + BUFFER and return the actual read number if bytes in NREAD. SEQNO + is the sequence number used to send the request and EXPECTED_TYPE + the type of message we expect. Does checks on the ccid + header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to + avoid debug messages in case of no error. Returns 0 on success. */ +static int +bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, + size_t *nread, int expected_type, int seqno, int timeout, + int no_debug) +{ + int i, rc; + size_t msglen; + + /* Fixme: The next line for the current Valgrind without support + for USB IOCTLs. */ + memset (buffer, 0, length); + retry: + rc = usb_bulk_read (handle->idev, + handle->ep_bulk_in, + (char*)buffer, length, + timeout); + if (rc < 0) + { + DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno)); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + + *nread = msglen = rc; + + if (msglen < 10) + { + DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen); + return CCID_DRIVER_ERR_INV_VALUE; + } + if (buffer[0] != expected_type) + { + DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]); + return CCID_DRIVER_ERR_INV_VALUE; + } + if (buffer[5] != 0) + { + DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]); + return CCID_DRIVER_ERR_INV_VALUE; + } + if (buffer[6] != seqno) + { + DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n", + seqno, buffer[6]); + return CCID_DRIVER_ERR_INV_VALUE; + } + + if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80) + { + /* Card present and active, time extension requested. */ + DEBUGOUT_2 ("time extension requested (%02X,%02X)\n", + buffer[7], buffer[8]); + goto retry; + } + + if (!no_debug) + { + DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n" + " data:", buffer[7], buffer[8], buffer[9] ); + for (i=10; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", buffer[i]); + DEBUGOUT_LF (); + } + if (CCID_COMMAND_FAILED (buffer)) + print_command_failed (buffer); + + /* Check whether a card is at all available. Note: If you add new + error codes here, check whether they need to be ignored in + send_escape_cmd. */ + switch ((buffer[7] & 0x03)) + { + case 0: /* no error */ break; + case 1: return CCID_DRIVER_ERR_CARD_INACTIVE; + case 2: return CCID_DRIVER_ERR_NO_CARD; + case 3: /* RFU */ break; + } + return 0; +} + + +/* Note that this function won't return the error codes NO_CARD or + CARD_INACTIVE. IF RESULT is not NULL, the result from the + operation will get returned in RESULT and its length in RESULTLEN. + If the response is larger than RESULTMAX, an error is returned and + the required buffer length returned in RESULTLEN. */ +static int +send_escape_cmd (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *result, size_t resultmax, size_t *resultlen) +{ + int i, rc; + unsigned char msg[100]; + size_t msglen; + unsigned char seqno; + + if (resultlen) + *resultlen = 0; + + if (datalen > sizeof msg - 10) + return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */ + + msg[0] = PC_to_RDR_Escape; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; /* RFU */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + memcpy (msg+10, data, datalen); + msglen = 10 + datalen; + set_msg_len (msg, datalen); + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape, + seqno, 5000, 0); + if (result) + switch (rc) + { + /* We need to ignore certain errorcode here. */ + case 0: + case CCID_DRIVER_ERR_CARD_INACTIVE: + case CCID_DRIVER_ERR_NO_CARD: + { + if (msglen < 10 || (msglen-10) > resultmax ) + rc = CCID_DRIVER_ERR_INV_VALUE; /* Invalid length of response. */ + else + { + memcpy (result, msg+10, msglen-10); + *resultlen = msglen-10; + } + rc = 0; + } + break; + default: + break; + } + + return rc; +} + + +int +ccid_transceive_escape (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *resp, size_t maxresplen, size_t *nresp) +{ + return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp); +} + + + +/* experimental */ +int +ccid_poll (ccid_driver_t handle) +{ + int rc; + unsigned char msg[10]; + size_t msglen; + int i, j; + + rc = usb_bulk_read (handle->idev, + handle->ep_intr, + (char*)msg, sizeof msg, + 0 /* ms timeout */ ); + if (rc < 0 && errno == ETIMEDOUT) + return 0; + + if (rc < 0) + { + DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno)); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + + msglen = rc; + rc = 0; + + if (msglen < 1) + { + DEBUGOUT ("intr-in msg too short\n"); + return CCID_DRIVER_ERR_INV_VALUE; + } + + if (msg[0] == RDR_to_PC_NotifySlotChange) + { + DEBUGOUT ("notify slot change:"); + for (i=1; i < msglen; i++) + for (j=0; j < 4; j++) + DEBUGOUT_CONT_3 (" %d:%c%c", + (i-1)*4+j, + (msg[i] & (1<<(j*2)))? 'p':'-', + (msg[i] & (2<<(j*2)))? '*':' '); + DEBUGOUT_LF (); + } + else if (msg[0] == RDR_to_PC_HardwareError) + { + DEBUGOUT ("hardware error occured\n"); + } + else + { + DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]); + } + + return 0; +} + + +/* Note that this fucntion won't return the error codes NO_CARD or + CARD_INACTIVE */ +int +ccid_slot_status (ccid_driver_t handle, int *statusbits) +{ + int rc; + unsigned char msg[100]; + size_t msglen; + unsigned char seqno; + int retries = 0; + + retry: + msg[0] = PC_to_RDR_GetSlotStatus; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; /* RFU */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, 0); + + rc = bulk_out (handle, msg, 10); + if (rc) + return rc; + /* Note that we set the NO_DEBUG flag here, so that the logs won't + get cluttered up by a ticker function checking for the slot + status and debugging enabled. */ + rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, + seqno, retries? 1000 : 200, 1); + if (rc == CCID_DRIVER_ERR_CARD_IO_ERROR && retries < 3) + { + if (!retries) + { + DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n"); + usb_clear_halt (handle->idev, handle->ep_bulk_in); + usb_clear_halt (handle->idev, handle->ep_bulk_out); + } + else + DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n"); + retries++; + goto retry; + } + if (rc && rc != CCID_DRIVER_ERR_NO_CARD + && rc != CCID_DRIVER_ERR_CARD_INACTIVE) + return rc; + *statusbits = (msg[7] & 3); + + return 0; +} + + +int +ccid_get_atr (ccid_driver_t handle, + unsigned char *atr, size_t maxatrlen, size_t *atrlen) +{ + int rc; + int statusbits; + unsigned char msg[100]; + unsigned char *tpdu; + size_t msglen, tpdulen; + unsigned char seqno; + int use_crc = 0; + unsigned int edc; + int i; + int tried_iso = 0; + + /* First check whether a card is available. */ + rc = ccid_slot_status (handle, &statusbits); + if (rc) + return rc; + if (statusbits == 2) + return CCID_DRIVER_ERR_NO_CARD; + + /* For an inactive and also for an active card, issue the PowerOn + command to get the ATR. */ + again: + msg[0] = PC_to_RDR_IccPowerOn; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, 0); + msglen = 10; + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock, + seqno, 5000, 0); + if (rc) + return rc; + if (!tried_iso && CCID_COMMAND_FAILED (msg) && CCID_ERROR_CODE (msg) == 0xbb + && ((handle->id_vendor == VENDOR_CHERRY + && handle->id_product == 0x0005) + || (handle->id_vendor == VENDOR_GEMPC + && handle->id_product == 0x4433) + )) + { + tried_iso = 1; + /* Try switching to ISO mode. */ + if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2, + NULL, 0, NULL)) + goto again; + } + else if (CCID_COMMAND_FAILED (msg)) + return CCID_DRIVER_ERR_CARD_IO_ERROR; + + + handle->powered_off = 0; + + if (atr) + { + size_t n = msglen - 10; + + if (n > maxatrlen) + n = maxatrlen; + memcpy (atr, msg+10, n); + *atrlen = n; + } + + /* Setup parameters to select T=1. */ + msg[0] = PC_to_RDR_SetParameters; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 1; /* Select T=1. */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + + /* FIXME: Get those values from the ATR. */ + msg[10]= 0x01; /* Fi/Di */ + msg[11]= 0x10; /* LRC, direct convention. */ + msg[12]= 0; /* Extra guardtime. */ + msg[13]= 0x41; /* BWI/CWI */ + msg[14]= 0; /* No clock stoppping. */ + msg[15]= 254; /* IFSC */ + msg[16]= 0; /* Does not support non default NAD values. */ + set_msg_len (msg, 7); + msglen = 10 + 7; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + /* Note that we ignore the error code on purpose. */ + bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, + seqno, 5000, 0); + + handle->t1_ns = 0; + handle->t1_nr = 0; + + /* Send an S-Block with our maximun IFSD to the CCID. */ + if (!handle->auto_ifsd) + { + tpdu = msg+10; + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; + tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */ + tpdu[2] = 1; + tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32; + tpdulen = 4; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + + msg[0] = PC_to_RDR_XfrBlock; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, tpdulen); + msglen = 10 + tpdulen; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + if (debug_level > 1) + DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) + : !!(msg[11] & 0x40)), + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + + + rc = bulk_in (handle, msg, sizeof msg, &msglen, + RDR_to_PC_DataBlock, seqno, 5000, 0); + if (rc) + return rc; + + tpdu = msg + 10; + tpdulen = msglen - 10; + + if (tpdulen < 4) + return CCID_DRIVER_ERR_ABORTED; + + if (debug_level > 1) + DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) + : !!(msg[11] & 0x40)), + ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1) + { + DEBUGOUT ("invalid response for S-block (Change-IFSD)\n"); + return -1; + } + DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]); + } + + return 0; +} + + + + +static unsigned int +compute_edc (const unsigned char *data, size_t datalen, int use_crc) +{ + if (use_crc) + { + return 0x42; /* Not yet implemented. */ + } + else + { + unsigned char crc = 0; + + for (; datalen; datalen--) + crc ^= *data++; + return crc; + } +} + + +/* Helper for ccid_transceive used for APDU level exchanges. */ +static int +ccid_transceive_apdu_level (ccid_driver_t handle, + const unsigned char *apdu_buf, size_t apdu_buflen, + unsigned char *resp, size_t maxresplen, + size_t *nresp) +{ + int rc; + unsigned char send_buffer[10+259], recv_buffer[10+259]; + const unsigned char *apdu; + size_t apdulen; + unsigned char *msg; + size_t msglen; + unsigned char seqno; + int i; + + msg = send_buffer; + + apdu = apdu_buf; + apdulen = apdu_buflen; + assert (apdulen); + + if (apdulen > 254) + return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ + + msg[0] = PC_to_RDR_XfrBlock; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 4; /* bBWI */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + memcpy (msg+10, apdu, apdulen); + set_msg_len (msg, apdulen); + msglen = 10 + apdulen; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + + msg = recv_buffer; + rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, + RDR_to_PC_DataBlock, seqno, 5000, 0); + if (rc) + return rc; + + apdu = msg + 10; + apdulen = msglen - 10; + + if (resp) + { + if (apdulen > maxresplen) + { + DEBUGOUT_2 ("provided buffer too short for received data " + "(%u/%u)\n", + (unsigned int)apdulen, (unsigned int)maxresplen); + return CCID_DRIVER_ERR_INV_VALUE; + } + + memcpy (resp, apdu, apdulen); + *nresp = apdulen; + } + + return 0; +} + + + +/* + Protocol T=1 overview + + Block Structure: + Prologue Field: + 1 byte Node Address (NAD) + 1 byte Protocol Control Byte (PCB) + 1 byte Length (LEN) + Information Field: + 0-254 byte APDU or Control Information (INF) + Epilogue Field: + 1 byte Error Detection Code (EDC) + + NAD: + bit 7 unused + bit 4..6 Destination Node Address (DAD) + bit 3 unused + bit 2..0 Source Node Address (SAD) + + If node adresses are not used, SAD and DAD should be set to 0 on + the first block sent to the card. If they are used they should + have different values (0 for one is okay); that first block sets up + the addresses of the nodes. + + PCB: + Information Block (I-Block): + bit 7 0 + bit 6 Sequence number (yep, that is modulo 2) + bit 5 Chaining flag + bit 4..0 reserved + Received-Ready Block (R-Block): + bit 7 1 + bit 6 0 + bit 5 0 + bit 4 Sequence number + bit 3..0 0 = no error + 1 = EDC or parity error + 2 = other error + other values are reserved + Supervisory Block (S-Block): + bit 7 1 + bit 6 1 + bit 5 clear=request,set=response + bit 4..0 0 = resyncronisation request + 1 = information field size request + 2 = abort request + 3 = extension of BWT request + 4 = VPP error + other values are reserved + +*/ + +int +ccid_transceive (ccid_driver_t handle, + const unsigned char *apdu_buf, size_t apdu_buflen, + unsigned char *resp, size_t maxresplen, size_t *nresp) +{ + int rc; + unsigned char send_buffer[10+259], recv_buffer[10+259]; + const unsigned char *apdu; + size_t apdulen; + unsigned char *msg, *tpdu, *p; + size_t msglen, tpdulen, last_tpdulen, n; + unsigned char seqno; + int i; + unsigned int edc; + int use_crc = 0; + size_t dummy_nresp; + int next_chunk = 1; + int sending = 1; + int retries = 0; + + if (!nresp) + nresp = &dummy_nresp; + *nresp = 0; + + /* Smarter readers allow to send APDUs directly; divert here. */ + if (handle->apdu_level) + return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen, + resp, maxresplen, nresp); + + /* The other readers we support require sending TPDUs. */ + + tpdulen = 0; /* Avoid compiler warning about no initialization. */ + msg = send_buffer; + for (;;) + { + if (next_chunk) + { + next_chunk = 0; + + apdu = apdu_buf; + apdulen = apdu_buflen; + assert (apdulen); + + /* Construct an I-Block. */ + if (apdulen > 254) + return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ + + tpdu = msg+10; + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; + tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */ + if (apdulen > 128 /* fixme: replace by ifsc */) + { + apdulen = 128; + apdu_buf += 128; + apdu_buflen -= 128; + tpdu[1] |= (1 << 5); /* Set more bit. */ + } + tpdu[2] = apdulen; + memcpy (tpdu+3, apdu, apdulen); + tpdulen = 3 + apdulen; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + } + + msg[0] = PC_to_RDR_XfrBlock; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 4; /* bBWI */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, tpdulen); + msglen = 10 + tpdulen; + last_tpdulen = tpdulen; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + if (debug_level > 1) + DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) + : !!(msg[11] & 0x40)), + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + + msg = recv_buffer; + rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, + RDR_to_PC_DataBlock, seqno, 5000, 0); + if (rc) + return rc; + + tpdu = msg + 10; + tpdulen = msglen - 10; + + if (tpdulen < 4) + { + usb_clear_halt (handle->idev, handle->ep_bulk_in); + return CCID_DRIVER_ERR_ABORTED; + } + + if (debug_level > 1) + DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), + ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + if (!(tpdu[1] & 0x80)) + { /* This is an I-block. */ + retries = 0; + if (sending) + { /* last block sent was successful. */ + handle->t1_ns ^= 1; + sending = 0; + } + + if (!!(tpdu[1] & 0x40) != handle->t1_nr) + { /* Reponse does not match our sequence number. */ + msg = send_buffer; + tpdu = msg+10; + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; + tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */ + tpdu[2] = 0; + tpdulen = 3; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + + continue; + } + + handle->t1_nr ^= 1; + + p = tpdu + 3; /* Skip the prologue field. */ + n = tpdulen - 3 - 1; /* Strip the epilogue field. */ + /* fixme: verify the checksum. */ + if (resp) + { + if (n > maxresplen) + { + DEBUGOUT_2 ("provided buffer too short for received data " + "(%u/%u)\n", + (unsigned int)n, (unsigned int)maxresplen); + return CCID_DRIVER_ERR_INV_VALUE; + } + + memcpy (resp, p, n); + resp += n; + *nresp += n; + maxresplen -= n; + } + + if (!(tpdu[1] & 0x20)) + return 0; /* No chaining requested - ready. */ + + msg = send_buffer; + tpdu = msg+10; + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; + tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */ + tpdu[2] = 0; + tpdulen = 3; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + } + else if ((tpdu[1] & 0xc0) == 0x80) + { /* This is a R-block. */ + if ( (tpdu[1] & 0x0f)) + { /* Error: repeat last block */ + if (++retries > 3) + { + DEBUGOUT ("3 failed retries\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + msg = send_buffer; + tpdulen = last_tpdulen; + } + else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns) + { /* Response does not match our sequence number. */ + DEBUGOUT ("R-block with wrong seqno received on more bit\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + else if (sending) + { /* Send next chunk. */ + retries = 0; + msg = send_buffer; + next_chunk = 1; + handle->t1_ns ^= 1; + } + else + { + DEBUGOUT ("unexpected ACK R-block received\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + } + else + { /* This is a S-block. */ + retries = 0; + DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n", + (tpdu[1] & 0x20)? "response": "request", + (tpdu[1] & 0x1f)); + if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2]) + { /* Wait time extension request. */ + unsigned char bwi = tpdu[3]; + msg = send_buffer; + tpdu = msg+10; + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; + tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */ + tpdu[2] = 1; + tpdu[3] = bwi; + tpdulen = 4; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + DEBUGOUT_1 ("T=1 waittime extension of bwi=%d\n", bwi); + } + else + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + } /* end T=1 protocol loop. */ + + return 0; +} + + +/* Send the CCID Secure command to the reader. APDU_BUF should + contain the APDU template. PIN_MODE defines how the pin gets + formatted: + + 1 := The PIN is ASCII encoded and of variable length. The + length of the PIN entered will be put into Lc by the reader. + The APDU should me made up of 4 bytes without Lc. + + PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0 + may be used t enable reasonable defaults. PIN_PADLEN should be 0. + + When called with RESP and NRESP set to NULL, the function will + merely check whether the reader supports the secure command for the + given APDU and PIN_MODE. */ +int +ccid_transceive_secure (ccid_driver_t handle, + const unsigned char *apdu_buf, size_t apdu_buflen, + int pin_mode, int pinlen_min, int pinlen_max, + int pin_padlen, + unsigned char *resp, size_t maxresplen, size_t *nresp) +{ + int rc; + unsigned char send_buffer[10+259], recv_buffer[10+259]; + unsigned char *msg, *tpdu, *p; + size_t msglen, tpdulen, n; + unsigned char seqno; + int i; + size_t dummy_nresp; + int testmode; + + testmode = !resp && !nresp; + + if (!nresp) + nresp = &dummy_nresp; + *nresp = 0; + + if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1)) + ; + else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2)) + return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */ + else + return CCID_DRIVER_ERR_NO_KEYPAD; + + if (pin_mode != 1) + return CCID_DRIVER_ERR_NOT_SUPPORTED; + + if (pin_padlen != 0) + return CCID_DRIVER_ERR_NOT_SUPPORTED; + + if (!pinlen_min) + pinlen_min = 1; + if (!pinlen_max) + pinlen_max = 25; + + /* Note that the 25 is the maximum value the SPR532 allows. */ + if (pinlen_min < 1 || pinlen_min > 25 + || pinlen_max < 1 || pinlen_max > 25 + || pinlen_min > pinlen_max) + return CCID_DRIVER_ERR_INV_VALUE; + + /* We have only tested this with an SCM reader so better don't risk + anything and do not allow the use with other readers. */ + if (handle->id_vendor != VENDOR_SCM) + return CCID_DRIVER_ERR_NOT_SUPPORTED; + + if (testmode) + return 0; /* Success */ + + msg = send_buffer; + if (handle->id_vendor == VENDOR_SCM) + { + DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n"); + rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3, + NULL, 0, NULL); + if (rc) + return rc; + } + + msg[0] = PC_to_RDR_Secure; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 4; /* bBWI */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + msg[10] = 0; /* Perform PIN verification. */ + msg[11] = 0; /* Timeout in seconds. */ + msg[12] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */ + if (handle->id_vendor == VENDOR_SCM) + { + /* For the SPR532 the next 2 bytes need to be zero. We do this + for all SCM product. Kudos to Martin Paljak for this + hint. */ + msg[13] = msg[14] = 0; + } + else + { + msg[13] = 0x00; /* bmPINBlockString: + 0 bits of pin length to insert. + 0 bytes of PIN block size. */ + msg[14] = 0x00; /* bmPINLengthFormat: + Units are bytes, position is 0. */ + } + msg[15] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */ + msg[16] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */ + msg[17] = 0x02; /* bEntryValidationCondition: + Validation key pressed */ + if (pinlen_min && pinlen_max && pinlen_min == pinlen_max) + msg[17] |= 0x01; /* Max size reached. */ + msg[18] = 0xff; /* bNumberMessage: Default. */ + msg[19] = 0x04; /* wLangId-High. */ + msg[20] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */ + msg[21] = 0; /* bMsgIndex. */ + /* bTeoProlog follows: */ + msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0; + msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */ + msg[24] = 4; /* apdulen. */ + /* APDU follows: */ + msg[25] = apdu_buf[0]; /* CLA */ + msg[26] = apdu_buf[1]; /* INS */ + msg[27] = apdu_buf[2]; /* P1 */ + msg[28] = apdu_buf[3]; /* P2 */ + msglen = 29; + set_msg_len (msg, msglen - 10); + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + + msg = recv_buffer; + rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, + RDR_to_PC_DataBlock, seqno, 5000, 0); + if (rc) + return rc; + + tpdu = msg + 10; + tpdulen = msglen - 10; + + if (tpdulen < 4) + { + usb_clear_halt (handle->idev, handle->ep_bulk_in); + return CCID_DRIVER_ERR_ABORTED; + } + if (debug_level > 1) + DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), + ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + if (!(tpdu[1] & 0x80)) + { /* This is an I-block. */ + /* Last block sent was successful. */ + handle->t1_ns ^= 1; + + if (!!(tpdu[1] & 0x40) != handle->t1_nr) + { /* Reponse does not match our sequence number. */ + DEBUGOUT ("I-block with wrong seqno received\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + + handle->t1_nr ^= 1; + + p = tpdu + 3; /* Skip the prologue field. */ + n = tpdulen - 3 - 1; /* Strip the epilogue field. */ + /* fixme: verify the checksum. */ + if (resp) + { + if (n > maxresplen) + { + DEBUGOUT_2 ("provided buffer too short for received data " + "(%u/%u)\n", + (unsigned int)n, (unsigned int)maxresplen); + return CCID_DRIVER_ERR_INV_VALUE; + } + + memcpy (resp, p, n); + resp += n; + *nresp += n; + maxresplen -= n; + } + + if (!(tpdu[1] & 0x20)) + return 0; /* No chaining requested - ready. */ + + DEBUGOUT ("chaining requested but not supported for Secure operation\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + else if ((tpdu[1] & 0xc0) == 0x80) + { /* This is a R-block. */ + if ( (tpdu[1] & 0x0f)) + { /* Error: repeat last block */ + DEBUGOUT ("No retries supported for Secure operation\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + else if (!!(tpdu[1] & 0x10) == handle->t1_ns) + { /* Reponse does not match our sequence number. */ + DEBUGOUT ("R-block with wrong seqno received on more bit\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + else + { /* Send next chunk. */ + DEBUGOUT ("chaining not supported on Secure operation\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + } + else + { /* This is a S-block. */ + DEBUGOUT_2 ("T=1 S-block %s received cmd=%d for Secure operation\n", + (tpdu[1] & 0x20)? "response": "request", + (tpdu[1] & 0x1f)); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + + return 0; +} + + + + +#ifdef TEST + + +static void +print_error (int err) +{ + const char *p; + char buf[50]; + + switch (err) + { + case 0: p = "success"; + case CCID_DRIVER_ERR_OUT_OF_CORE: p = "out of core"; break; + case CCID_DRIVER_ERR_INV_VALUE: p = "invalid value"; break; + case CCID_DRIVER_ERR_NO_DRIVER: p = "no driver"; break; + case CCID_DRIVER_ERR_NOT_SUPPORTED: p = "not supported"; break; + case CCID_DRIVER_ERR_LOCKING_FAILED: p = "locking failed"; break; + case CCID_DRIVER_ERR_BUSY: p = "busy"; break; + case CCID_DRIVER_ERR_NO_CARD: p = "no card"; break; + case CCID_DRIVER_ERR_CARD_INACTIVE: p = "card inactive"; break; + case CCID_DRIVER_ERR_CARD_IO_ERROR: p = "card I/O error"; break; + case CCID_DRIVER_ERR_GENERAL_ERROR: p = "general error"; break; + case CCID_DRIVER_ERR_NO_READER: p = "no reader"; break; + case CCID_DRIVER_ERR_ABORTED: p = "aborted"; break; + default: sprintf (buf, "0x%05x", err); p = buf; break; + } + fprintf (stderr, "operation failed: %s\n", p); +} + +static void +print_data (const unsigned char *data, size_t length) +{ + if (length >= 2) + { + fprintf (stderr, "operation status: %02X%02X\n", + data[length-2], data[length-1]); + length -= 2; + } + if (length) + { + fputs (" returned data:", stderr); + for (; length; length--, data++) + fprintf (stderr, " %02X", *data); + putc ('\n', stderr); + } +} + +static void +print_result (int rc, const unsigned char *data, size_t length) +{ + if (rc) + print_error (rc); + else if (data) + print_data (data, length); +} + +int +main (int argc, char **argv) +{ + int rc; + ccid_driver_t ccid; + unsigned int slotstat; + unsigned char result[512]; + size_t resultlen; + int no_pinpad = 0; + int verify_123456 = 0; + int did_verify = 0; + int no_poll = 0; + + if (argc) + { + argc--; + argv++; + } + + while (argc) + { + if ( !strcmp (*argv, "--list")) + { + char *p; + p = ccid_get_reader_list (); + if (!p) + return 1; + fputs (p, stderr); + free (p); + return 0; + } + else if ( !strcmp (*argv, "--debug")) + { + ccid_set_debug_level (1); + argc--; argv++; + } + else if ( !strcmp (*argv, "--no-poll")) + { + no_poll = 1; + argc--; argv++; + } + else if ( !strcmp (*argv, "--no-pinpad")) + { + no_pinpad = 1; + argc--; argv++; + } + else if ( !strcmp (*argv, "--verify-123456")) + { + verify_123456 = 1; + argc--; argv++; + } + else + break; + } + + rc = ccid_open_reader (&ccid, argc? *argv:NULL); + if (rc) + return 1; + + if (!no_poll) + ccid_poll (ccid); + fputs ("getting ATR ...\n", stderr); + rc = ccid_get_atr (ccid, NULL, 0, NULL); + if (rc) + { + print_error (rc); + return 1; + } + + if (!no_poll) + ccid_poll (ccid); + fputs ("getting slot status ...\n", stderr); + rc = ccid_slot_status (ccid, &slotstat); + if (rc) + { + print_error (rc); + return 1; + } + + if (!no_poll) + ccid_poll (ccid); + + fputs ("selecting application OpenPGP ....\n", stderr); + { + static unsigned char apdu[] = { + 0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01}; + rc = ccid_transceive (ccid, + apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (rc, result, resultlen); + } + + + if (!no_poll) + ccid_poll (ccid); + + fputs ("getting OpenPGP DO 0x65 ....\n", stderr); + { + static unsigned char apdu[] = { 0, 0xCA, 0, 0x65, 254 }; + rc = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (rc, result, resultlen); + } + + if (!no_pinpad) + { + } + + if (!no_pinpad) + { + static unsigned char apdu[] = { 0, 0x20, 0, 0x81 }; + + + if (ccid_transceive_secure (ccid, + apdu, sizeof apdu, + 1, 0, 0, 0, + NULL, 0, NULL)) + fputs ("can't verify using a PIN-Pad reader\n", stderr); + else + { + fputs ("verifying CHV1 using the PINPad ....\n", stderr); + + rc = ccid_transceive_secure (ccid, + apdu, sizeof apdu, + 1, 0, 0, 0, + result, sizeof result, &resultlen); + print_result (rc, result, resultlen); + did_verify = 1; + } + } + + if (verify_123456 && !did_verify) + { + fputs ("verifying that CHV1 is 123456....\n", stderr); + { + static unsigned char apdu[] = {0, 0x20, 0, 0x81, + 6, '1','2','3','4','5','6'}; + rc = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (rc, result, resultlen); + } + } + + if (!rc) + { + fputs ("getting OpenPGP DO 0x5E ....\n", stderr); + { + static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 }; + rc = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (rc, result, resultlen); + } + } + + ccid_close_reader (ccid); + + return 0; +} + +/* + * Local Variables: + * compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c" + * End: + */ +#endif /*TEST*/ +#endif /*HAVE_LIBUSB*/ diff --git a/ccid/ccid-driver.h b/ccid/ccid-driver.h new file mode 100644 index 0000000..34c27cc --- /dev/null +++ b/ccid/ccid-driver.h @@ -0,0 +1,108 @@ +/* ccid-driver.c - USB ChipCardInterfaceDevices driver + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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 2 of the License, or + * (at your option) any later version. + * + * GnuPG 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU General Public License. If you wish to + * allow use of your version of this file only under the terms of the + * GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS 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. + * + * $Id: ccid-driver.h,v 1.1 2006-01-19 23:21:47 hackbard Exp $ + */ + +#ifndef CCID_DRIVER_H +#define CCID_DRIVER_H + +/* The CID driver returns the same error codes as the status words + used by GnuPG's apdu.h. For ease of maintenance they should always + match. */ +#define CCID_DRIVER_ERR_OUT_OF_CORE 0x10001 +#define CCID_DRIVER_ERR_INV_VALUE 0x10002 +#define CCID_DRIVER_ERR_INCOMPLETE_CARD_RESPONSE = 0x10003 +#define CCID_DRIVER_ERR_NO_DRIVER 0x10004 +#define CCID_DRIVER_ERR_NOT_SUPPORTED 0x10005 +#define CCID_DRIVER_ERR_LOCKING_FAILED 0x10006 +#define CCID_DRIVER_ERR_BUSY 0x10007 +#define CCID_DRIVER_ERR_NO_CARD 0x10008 +#define CCID_DRIVER_ERR_CARD_INACTIVE 0x10009 +#define CCID_DRIVER_ERR_CARD_IO_ERROR 0x1000a +#define CCID_DRIVER_ERR_GENERAL_ERROR 0x1000b +#define CCID_DRIVER_ERR_NO_READER 0x1000c +#define CCID_DRIVER_ERR_ABORTED 0x1000d +#define CCID_DRIVER_ERR_NO_KEYPAD 0x1000e + +struct ccid_driver_s; +typedef struct ccid_driver_s *ccid_driver_t; + +int ccid_set_debug_level (int level); +char *ccid_get_reader_list (void); +int ccid_open_reader (ccid_driver_t *handle, const char *readerid); +int ccid_shutdown_reader (ccid_driver_t handle); +int ccid_close_reader (ccid_driver_t handle); +int ccid_get_atr (ccid_driver_t handle, + unsigned char *atr, size_t maxatrlen, size_t *atrlen); +int ccid_slot_status (ccid_driver_t handle, int *statusbits); +int ccid_transceive (ccid_driver_t handle, + const unsigned char *apdu, size_t apdulen, + unsigned char *resp, size_t maxresplen, size_t *nresp); +int ccid_transceive_secure (ccid_driver_t handle, + const unsigned char *apdu, size_t apdulen, + int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen, + unsigned char *resp, size_t maxresplen, size_t *nresp); +int ccid_transceive_escape (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *resp, size_t maxresplen, + size_t *nresp); + + + +#endif /*CCID_DRIVER_H*/ + + + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..1125175 --- /dev/null +++ b/configure.in @@ -0,0 +1,15 @@ +dnl Process this file with autoconf to create configure. + +AC_INIT + +AC_CANONICAL_SYSTEM + +AM_INIT_AUTOMAKE(librfid, 0.0.1) + +AC_PROG_CC +AC_EXEEXT +AM_PROG_LIBTOOL +AC_SUBST(LIBTOOL_DEPS) + +dnl Output the makefile +AC_OUTPUT(Makefile src/Makefile include/Makefile include/librfid/Makefile utils/Makefile) diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..dfaa277 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = librfid diff --git a/include/librfid/Makefile.am b/include/librfid/Makefile.am new file mode 100644 index 0000000..59ce8cb --- /dev/null +++ b/include/librfid/Makefile.am @@ -0,0 +1,10 @@ + +pkginclude_HEADERS = rfid.h rfid_asic.h rfid_asic_rc632.h \ + rfid_layer2.h rfid_layer2_iso14443a.h \ + rfid_layer2_iso14443b.h rfid_layer2_iso15693.h \ + rfid_protocol.h rfid_protocol_tcl.h \ + rfid_protocol_mifare_ul.h \ + rfid_protocol_mifare_classic.h \ + rfid_reader.h \ + rfid_reader_cm5121.h + diff --git a/include/librfid/rfid.h b/include/librfid/rfid.h new file mode 100644 index 0000000..76e86e3 --- /dev/null +++ b/include/librfid/rfid.h @@ -0,0 +1,32 @@ +#ifndef _RFID_H +#define _RFID_H + +#include + +#ifdef __LIBRFID__ + +enum rfid_frametype { + RFID_14443A_FRAME_REGULAR, + RFID_14443B_FRAME_REGULAR, + RFID_MIFARE_FRAME, +}; + +#if 0 +#define DEBUGP(x, args ...) fprintf(stderr, "%s(%d):%s: " x, __FILE__, __LINE__, __FUNCTION__, ## args) +#define DEBUGPC(x, args ...) fprintf(stderr, x, ## args) +#else +#define DEBUGP(x, args ...) +#define DEBUGPC(x, args ...) +#endif + +extern const char *rfid_hexdump(const void *data, unsigned int len); + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#endif /* __LIBRFID__ */ + +int rfid_init(); + +#endif /* _RFID_H */ diff --git a/include/librfid/rfid_asic.h b/include/librfid/rfid_asic.h new file mode 100644 index 0000000..3686a7c --- /dev/null +++ b/include/librfid/rfid_asic.h @@ -0,0 +1,45 @@ +#ifndef _RFID_ASIC_H +#define _RFID_ASIC_H + +enum rfid_frametype; + +#include + +struct rfid_asic_transport { + char *name; + union { + struct rfid_asic_rc632_transport rc632; + } priv; +}; + +struct rfid_asic_transport_handle { + void *data; /* handle to stuff like even lower layers */ + + struct rfid_asic_transport *rat; +}; + + +struct rfid_asic_handle { + struct rfid_asic_transport_handle *rath; + unsigned int fc; + unsigned int mtu; + unsigned int mru; + + union { + struct rfid_asic_rc632_handle rc632; + //struct rfid_asic_rc531_handle rc531; + } priv; + struct rfid_asic *asic; +}; + + +struct rfid_asic { + char *name; + unsigned int fc; /* carrier frequency */ + union { + struct rfid_asic_rc632 rc632; + //struct rfid_asic_rc531 rc531; + } priv; +}; + +#endif /* _RFID_ASIC_H */ diff --git a/include/librfid/rfid_asic_rc632.h b/include/librfid/rfid_asic_rc632.h new file mode 100644 index 0000000..893e73b --- /dev/null +++ b/include/librfid/rfid_asic_rc632.h @@ -0,0 +1,160 @@ +#ifndef _RFID_ASIC_RC632_H +#define _RFID_ASIC_RC632_H + +struct rfid_asic_transport_handle; + +#include +#include + +struct rfid_asic_rc632_transport { + struct { + int (*reg_write)(struct rfid_asic_transport_handle *rath, + u_int8_t reg, + u_int8_t value); + int (*reg_read)(struct rfid_asic_transport_handle *rath, + u_int8_t reg, + u_int8_t *value); + int (*fifo_write)(struct rfid_asic_transport_handle *rath, + u_int8_t len, + const u_int8_t *buf, + u_int8_t flags); + int (*fifo_read)(struct rfid_asic_transport_handle *rath, + u_int8_t len, + u_int8_t *buf); + } fn; +}; + +struct rfid_asic_handle; + +struct iso14443a_atqa; +struct iso14443a_anticol_cmd; + +struct rfid_asic_rc632 { + struct { + int (*power_up)(struct rfid_asic_handle *h); + int (*power_down)(struct rfid_asic_handle *h); + int (*turn_on_rf)(struct rfid_asic_handle *h); + int (*turn_off_rf)(struct rfid_asic_handle *h); + int (*transcieve)(struct rfid_asic_handle *h, + enum rfid_frametype, + const u_int32_t *tx_buf, + unsigned int tx_len, + u_int32_t *rx_buf, + unsigned int *rx_len, + u_int64_t timeout, + unsigned int flags); + struct { + int (*init)(struct rfid_asic_handle *h); + int (*transcieve_sf)(struct rfid_asic_handle *h, + u_int32_t cmd, + struct iso14443a_atqa *atqa); + int (*transcieve_acf)(struct rfid_asic_handle *h, + struct iso14443a_anticol_cmd *cmd, + unsigned int *bit_of_col); + int (*set_speed)(struct rfid_asic_handle *h, + unsigned int tx, + unsigned int speed); + } iso14443a; + struct { + int (*init)(struct rfid_asic_handle *h); + } iso14443b; + struct { + int (*init)(struct rfid_asic_handle *h); + } iso15693; + struct { + int (*setkey)(struct rfid_asic_handle *h, + const unsigned char *key); + int (*auth)(struct rfid_asic_handle *h, u_int8_t cmd, + u_int32_t serno, u_int8_t block); + } mifare_classic; + } fn; +}; + +struct rc632_transport_handle { +}; + +/* A handle to a specific RC632 chip */ +struct rfid_asic_rc632_handle { + struct rc632_transport_handle th; +}; + +#if 0 +int +rc632_reg_write(struct rfid_asic_handle *handle, + u_int8_t reg, + u_int8_t val); + +int +rc632_reg_read(struct rfid_asic_handle *handle, + u_int8_t reg, + u_int8_t *val); +int +rc632_fifo_write(struct rfid_asic_handle *handle, + u_int8_t len, + const u_int32_t *buf, + u_int8_t flags); + +int +rc632_fifo_read(struct rfid_asic_handle *handle, + u_int8_t len, + u_int8_t *buf); + +int +rc632_set_bits(struct rfid_asic_handle *handle, u_int8_t reg, + u_int82_t val); + +int +rc632_clear_bits(struct rfid_asic_handle *handle, u_int32_t reg, + u_int32_t val); + + +int +rc632_turn_on_rf(struct rfid_asic_handle *handle); + + +int +rc632_turn_off_rf(struct rfid_asic_handle *handle); + +int +rc632_power_up(struct rfid_asic_handle *handle); + +int +rc632_power_down(struct rfid_asic_handle *handle); + + +int +rc632_wait_idle(struct rfid_asic_handle *handle, u_int64_t time); + +int +rc632_transmit(struct rfid_asic_handle *handle, + const u_int32_t *buf, + u_int32_t len, + u_int64_t timeout); + +int +rc632_transcieve(struct rfid_asic_handle *handle, + const u_int32_t *tx_buf, + u_int32_t tx_len, + u_int32_t *rx_buf, + u_int32_t *rx_len, + unsigned int timer, + unsigned int toggle); + +int +rc632_read_eeprom(struct rfid_asic_handle *handle); + + +int +rc632_calc_crc16_from(struct rfid_asic_handle *handle); + +int +rc632_register_dump(struct rfid_asic_handle *handle, u_int32_t *buf); + + +struct rfid_asic_handle * rc632_open(struct rfid_asic_transport_handle *th); + + +extern struct rfid_asic rc632; +#endif + +#endif diff --git a/include/librfid/rfid_layer2.h b/include/librfid/rfid_layer2.h new file mode 100644 index 0000000..4622a02 --- /dev/null +++ b/include/librfid/rfid_layer2.h @@ -0,0 +1,76 @@ +#ifndef _RFID_LAYER2_H +#define _RFID_LAYER2_H + +#include +#include + +struct rfid_layer2_handle; +struct rfid_reader_handle; + +enum rfid_layer2_id { + RFID_LAYER2_NONE, + RFID_LAYER2_ISO14443A, + RFID_LAYER2_ISO14443B, + RFID_LAYER2_ISO15693, +}; + +struct rfid_layer2_handle *rfid_layer2_init(struct rfid_reader_handle *rh, + unsigned int id); +int rfid_layer2_open(struct rfid_layer2_handle *l2h); +int rfid_layer2_transcieve(struct rfid_layer2_handle *l2h, + enum rfid_frametype frametype, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len, + u_int64_t timeout, unsigned int flags); +int rfid_layer2_close(struct rfid_layer2_handle *l2h); +int rfid_layer2_fini(struct rfid_layer2_handle *l2h); +int rfid_layer2_getopt(struct rfid_layer2_handle *ph, int optname, + void *optval, unsigned int *optlen); +int rfid_layer2_setopt(struct rfid_layer2_handle *ph, int optname, + const void *optval, unsigned int optlen); + +#ifdef __LIBRFID__ + +#include +#include +#include + +struct rfid_layer2 { + unsigned int id; + char *name; + + struct { + struct rfid_layer2_handle *(*init)(struct rfid_reader_handle *h); + int (*open)(struct rfid_layer2_handle *h); + int (*transcieve)(struct rfid_layer2_handle *h, + enum rfid_frametype frametype, + const unsigned char *tx_buf, + unsigned int tx_len, unsigned char *rx_buf, + unsigned int *rx_len, u_int64_t timeout, + unsigned int flags); + int (*close)(struct rfid_layer2_handle *h); + int (*fini)(struct rfid_layer2_handle *h); + int (*getopt)(struct rfid_layer2_handle *h, + int optname, void *optval, unsigned int *optlen); + int (*setopt)(struct rfid_layer2_handle *h, + int optname, const void *optval, + unsigned int optlen); + } fn; + struct rfid_layer2 *next; +}; + +struct rfid_layer2_handle { + struct rfid_reader_handle *rh; + unsigned char uid[10]; /* triple size 14443a id is 10 bytes */ + unsigned int uid_len; + union { + struct iso14443a_handle iso14443a; + struct iso14443b_handle iso14443b; + struct iso15693_handle iso15693; + } priv; + struct rfid_layer2 *l2; +}; + +#endif /* __LIBRFID__ */ + +#endif diff --git a/include/librfid/rfid_layer2_iso14443a.h b/include/librfid/rfid_layer2_iso14443a.h new file mode 100644 index 0000000..50a6f43 --- /dev/null +++ b/include/librfid/rfid_layer2_iso14443a.h @@ -0,0 +1,84 @@ +#ifndef _RFID_ISO14443A_H +#define _RFID_ISO14443A_H + +enum rfid_14443a_opt { + RFID_OPT_14443A_SPEED_RX = 0x00000001, + RFID_OPT_14443A_SPEED_TX = 0x00000002, +}; + +enum rfid_14443_opt_speed { + RFID_14443A_SPEED_106K = 0x01, + RFID_14443A_SPEED_212K = 0x02, + RFID_14443A_SPEED_424K = 0x04, + RFID_14443A_SPEED_848K = 0x08, +}; + +#ifdef __LIBRFID__ + +#include + +/* protocol definitions */ + +/* ISO 14443-3, Chapter 6.3.1 */ +enum iso14443a_sf_cmd { + ISO14443A_SF_CMD_REQA = 0x26, + ISO14443A_SF_CMD_WUPA = 0x52, + ISO14443A_SF_CMD_OPT_TIMESLOT = 0x35, /* Annex C */ + /* 40 to 4f and 78 to 7f: proprietary */ +}; + +struct iso14443a_atqa { + u_int8_t bf_anticol:5, + rfu1:1, + uid_size:2; + u_int8_t proprietary:4, + rfu2:4; +}; + +#define ISO14443A_HLTA 0x5000 + +/* ISO 14443-3, Chapter 6.3.2 */ +struct iso14443a_anticol_cmd { + unsigned char sel_code; + unsigned char nvb; + unsigned char uid_bits[5]; +}; + +enum iso14443a_anticol_sel_code { + ISO14443A_AC_SEL_CODE_CL1 = 0x93, + ISO14443A_AC_SEL_CODE_CL2 = 0x95, + ISO14443A_AC_SEL_CODE_CL3 = 0x97, +}; + +#define ISO14443A_BITOFCOL_NONE 0xffffffff + +struct iso14443a_handle { + unsigned int state; + unsigned int level; + unsigned int tcl_capable; +}; + +enum iso14443a_level { + ISO14443A_LEVEL_NONE, + ISO14443A_LEVEL_CL1, + ISO14443A_LEVEL_CL2, + ISO14443A_LEVEL_CL3, +}; + +enum iso14443a_state { + ISO14443A_STATE_ERROR, + ISO14443A_STATE_NONE, + ISO14443A_STATE_REQA_SENT, + ISO14443A_STATE_ATQA_RCVD, + ISO14443A_STATE_NO_BITFRAME_ANTICOL, + ISO14443A_STATE_ANTICOL_RUNNING, + ISO14443A_STATE_SELECTED, +}; + +#include +struct rfid_layer2 rfid_layer2_iso14443a; + +#endif /* __LIBRFID__ */ + + +#endif /* _ISO14443A_H */ diff --git a/include/librfid/rfid_layer2_iso14443b.h b/include/librfid/rfid_layer2_iso14443b.h new file mode 100644 index 0000000..5d6d979 --- /dev/null +++ b/include/librfid/rfid_layer2_iso14443b.h @@ -0,0 +1,85 @@ +#ifndef _RFID_LAYER2_ISO14443B_H +#define _RFID_LAYER2_ISO14443B_H + +#ifdef __LIBRFID__ + +struct iso14443b_atqb { + unsigned char fifty; + unsigned char pupi[4]; + unsigned char app_data[4]; + struct { + unsigned char bit_rate_capability; + unsigned char protocol_type:4, + max_frame_size:4; + unsigned char fo:2, + adc:2, + fwi:4; + } protocol_info; +}; + +struct iso14443b_attrib_hdr { + unsigned char one_d; + unsigned char identifier[4]; + struct { + unsigned char rfu:2, + sof:1, + eof:1, + min_tr1:2, + min_tr0:2; + } param1; + struct { + unsigned char fsdi:4, + spd_out:2, + spd_in:2; + } param2; + struct { + unsigned char protocol_type:4, + rfu:4; + } param3; + struct { + unsigned char cid:4, + rfu:4; + } param4; +}; + +struct iso14443b_handle { + unsigned int tcl_capable; /* do we support T=CL */ + + unsigned int cid; /* Card ID */ + + unsigned int fsc; /* max. frame size card */ + unsigned int fsd; /* max. frame size reader */ + + unsigned int fwt; /* frame waiting time (in usec) */ + + unsigned int mbl; /* maximum buffer length */ + + unsigned int tr0; /* pcd-eof to picc-subcarrier-on */ + unsigned int tr1; /* picc-subcarrier-on to picc-sof */ + + unsigned int flags; + unsigned int state; +}; + +enum { + ISO14443B_CID_SUPPORTED = 0x01, + ISO14443B_NAD_SUPPORTED = 0x02, +}; + +enum { + ISO14443B_STATE_ERROR, + ISO14443B_STATE_NONE, + ISO14443B_STATE_REQB_SENT, + ISO14443B_STATE_ATQB_RCVD, + ISO14443B_STATE_ATTRIB_SENT, + ISO14443B_STATE_SELECTED, + ISO14443B_STATE_HLTB_SENT, + ISO14443B_STATE_HALTED, +}; + +#include +struct rfid_layer2 rfid_layer2_iso14443b; + +#endif /* __LIBRFID__ */ + +#endif diff --git a/include/librfid/rfid_layer2_iso15693.h b/include/librfid/rfid_layer2_iso15693.h new file mode 100644 index 0000000..ea7f87a --- /dev/null +++ b/include/librfid/rfid_layer2_iso15693.h @@ -0,0 +1,55 @@ +#ifndef _RFID_ISO15693_H +#define _RFID_ISO15693_H + +#include + +/* +07h = TagIt +04h = I.CODE +05h = Infineon +02h = ST +*/ + +/* protocol definitions */ + +struct iso15693_handle; + +struct iso15693_transport { + unsigned char *name; + + struct { + int (*init)(struct iso15693_handle *handle); + int (*fini)(struct iso15693_handle *handle); + +#if 0 + int (*transcieve_sf)(struct iso14443a_handle *handle, + unsigned char cmd, + struct iso14443a_atqa *atqa); + int (*transcieve_acf)(struct iso14443a_handle *handle, + struct iso14443a_anticol_cmd *acf, + unsigned int *bit_of_col); +#endif + int (*transcieve)(struct iso15693_handle *handle, + const unsigned char *tx_buf, + unsigned int tx_len, + unsigned char *rx_buf, + unsigned int *rx_len); + } fn; + + union { + } priv; +}; + +struct iso15693_handle { + unsigned int state; +}; + +enum iso15693_state { + ISO15693_STATE_ERROR, + ISO15693_STATE_NONE, +}; + +#include +extern struct rfid_layer2 rfid_layer2_iso15693; + +#endif /* _ISO15693_H */ diff --git a/include/librfid/rfid_protocol.h b/include/librfid/rfid_protocol.h new file mode 100644 index 0000000..65bda4c --- /dev/null +++ b/include/librfid/rfid_protocol.h @@ -0,0 +1,88 @@ +#ifndef _RFID_PROTOCOL_H +#define _RFID_PROTOCOL_H + +#include + +struct rfid_protocol_handle; + +struct rfid_protocol_handle * +rfid_protocol_init(struct rfid_layer2_handle *l2h, unsigned int id); +int rfid_protocol_open(struct rfid_protocol_handle *ph); +int rfid_protocol_transcieve(struct rfid_protocol_handle *ph, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len, + unsigned int timeout, unsigned int flags); +int +rfid_protocol_read(struct rfid_protocol_handle *ph, + unsigned int page, + unsigned char *rx_data, + unsigned int *rx_len); + +int +rfid_protocol_write(struct rfid_protocol_handle *ph, + unsigned int page, + unsigned char *tx_data, + unsigned int tx_len); + +int rfid_protocol_fini(struct rfid_protocol_handle *ph); +int rfid_protocol_close(struct rfid_protocol_handle *ph); + +enum rfid_protocol_id { + RFID_PROTOCOL_UNKNOWN, + RFID_PROTOCOL_TCL, + RFID_PROTOCOL_MIFARE_UL, + RFID_PROTOCOL_MIFARE_CLASSIC, +}; + + +#ifdef __LIBRFID__ + +struct rfid_protocol { + struct rfid_protocol *next; + unsigned int id; + char *name; + struct { + struct rfid_protocol_handle *(*init)(struct rfid_layer2_handle *l2h); + int (*open)(struct rfid_protocol_handle *ph); + int (*close)(struct rfid_protocol_handle *ph); + int (*fini)(struct rfid_protocol_handle *ph); + /* transcieve for session based transport protocols */ + int (*transcieve)(struct rfid_protocol_handle *ph, + const unsigned char *tx_buf, + unsigned int tx_len, + unsigned char *rx_buf, + unsigned int *rx_len, + unsigned int timeout, + unsigned int flags); + /* read/write for synchronous memory cards */ + int (*read)(struct rfid_protocol_handle *ph, + unsigned int page, + unsigned char *rx_data, + unsigned int *rx_len); + int (*write)(struct rfid_protocol_handle *ph, + unsigned int page, + unsigned char *tx_data, + unsigned int tx_len); + } fn; +}; + +int rfid_protocol_register(struct rfid_protocol *p); + +#include +#include +#include + +struct rfid_protocol_handle { + struct rfid_layer2_handle *l2h; + struct rfid_protocol *proto; + union { + struct tcl_handle tcl; + } priv; /* priv has to be last, since + * it could contain additional + * private data over the end of + * sizeof(priv). */ +}; + +#endif /* __LIBRFID__ */ + +#endif /* _RFID_PROTOCOL_H */ diff --git a/include/librfid/rfid_protocol_mifare_classic.h b/include/librfid/rfid_protocol_mifare_classic.h new file mode 100644 index 0000000..e6b2400 --- /dev/null +++ b/include/librfid/rfid_protocol_mifare_classic.h @@ -0,0 +1,28 @@ +#ifndef _MIFARE_CLASSIC_H + +#define MIFARE_CL_KEYA_DEFAULT "\xa0\xa1\xa2\xa3\xa4\xa5" +#define MIFARE_CL_KEYB_DEFAULT "\xb0\xb1\xb2\xb3\xb4\xb5" + +#define MIFARE_CL_KEYA_DEFAULT_INFINEON "\xff\xff\xff\xff\xff\xff" +#define MIFARE_CL_KEYB_DEFAULT_INFINEON MIFARE_CL_KEYA_DEFAULT_INFINEON + +#define MIFARE_CL_PAGE_MAX 0xff + +#define RFID_CMD_MIFARE_AUTH1A 0x60 +#define RFID_CMD_MIFARE_AUTH1B 0x61 + +#ifdef __LIBRFID__ + +extern struct rfid_protocol rfid_protocol_mfcl; + + +#define MIFARE_CL_CMD_WRITE16 0xA0 +#define MIFARE_CL_CMD_READ 0x30 + +#define MIFARE_CL_RESP_ACK 0x0a +#define MIFARE_CL_RESP_NAK 0x00 + + +#endif /* __LIBRFID__ */ + +#endif /* _MIFARE_CLASSIC_H */ diff --git a/include/librfid/rfid_protocol_mifare_ul.h b/include/librfid/rfid_protocol_mifare_ul.h new file mode 100644 index 0000000..cc93b6b --- /dev/null +++ b/include/librfid/rfid_protocol_mifare_ul.h @@ -0,0 +1,24 @@ +#ifndef _RFID_PROTOCOL_MFUL_H +#define _RFID_PROTOCOL_MFUL_H + +int rfid_mful_lock_page(struct rfid_protocol_handle *ph, unsigned int page); +int rfid_mful_lock_otp(struct rfid_protocol_handle *ph); + +#define MIFARE_UL_PAGE_MAX 15 + +#ifdef __LIBRFID__ + +#define MIFARE_UL_CMD_WRITE 0xA2 +#define MIFARE_UL_CMD_READ 0x30 + +#define MIFARE_UL_RESP_ACK 0x0a +#define MIFARE_UL_RESP_NAK 0x00 + +#define MIFARE_UL_PAGE_LOCK 2 +#define MIFARE_UL_PAGE_OTP 3 + +extern struct rfid_protocol rfid_protocol_mful; + +#endif /* __LIBRFID__ */ + +#endif diff --git a/include/librfid/rfid_protocol_tcl.h b/include/librfid/rfid_protocol_tcl.h new file mode 100644 index 0000000..f754ad9 --- /dev/null +++ b/include/librfid/rfid_protocol_tcl.h @@ -0,0 +1,70 @@ +#ifndef _RFID_PROTOCOL_TCL_H +#define _RFID_PROTOCOL_TCL_H + +#ifdef __LIBRFID__ + +enum tcl_transport_rate { + TCL_RATE_106 = 0x01, + TCL_RATE_212 = 0x02, + TCL_RATE_424 = 0x04, + TCL_RATE_848 = 0x08, +}; + +enum tcl_transport_transcieve_flags { + TCL_TRANSP_F_TX_CRC = 0x01, /* transport adds TX CRC */ + TCL_TRANSP_F_RX_CRC = 0x02, +}; + +struct tcl_handle { + /* derived from ats */ + unsigned char *historical_bytes; /* points into ats */ + unsigned int historical_len; + + unsigned int fsc; /* max frame size accepted by card */ + unsigned int fsd; /* max frame size accepted by reader */ + unsigned int fwt; /* frame waiting time (in usec)*/ + unsigned char ta; /* divisor information */ + unsigned char sfgt; /* start-up frame guard time (in usec) */ + + /* otherwise determined */ + unsigned int cid; /* Card ID */ + unsigned int nad; /* Node Address */ + + unsigned int flags; + unsigned int state; /* protocol state */ + + unsigned int toggle; /* send toggle with next frame */ + + unsigned int ats_len; + unsigned char ats[0]; +}; + +enum tcl_handle_flags { + TCL_HANDLE_F_NAD_SUPPORTED = 0x0001, + TCL_HANDLE_F_CID_SUPPORTED = 0x0002, + TCL_HANDLE_F_NAD_USED = 0x0010, + TCL_HANDLE_F_CID_USED = 0x0020, +}; + + +enum tcl_pcb_bits { + TCL_PCB_CID_FOLLOWING = 0x08, + TCL_PCB_NAD_FOLLOWING = 0x04, +}; + +enum tcl_pcd_state { + TCL_STATE_NONE = 0x00, + TCL_STATE_INITIAL, + TCL_STATE_RATS_SENT, /* waiting for ATS */ + TCL_STATE_ATS_RCVD, /* ATS received */ + TCL_STATE_PPS_SENT, /* waiting for PPS response */ + TCL_STATE_ESTABLISHED, /* xchg transparent data */ + TCL_STATE_DESELECT_SENT, /* waiting for DESELECT response */ + TCL_STATE_DESELECTED, /* card deselected or HLTA'd */ +}; + +struct rfid_protocol rfid_protocol_tcl; + +#endif /* __LIBRFID__ */ + +#endif diff --git a/include/librfid/rfid_reader.h b/include/librfid/rfid_reader.h new file mode 100644 index 0000000..3581cc1 --- /dev/null +++ b/include/librfid/rfid_reader.h @@ -0,0 +1,67 @@ +#ifndef _RFID_READER_H +#define _RFID_READER_H + +#include +#include + +struct rfid_reader_handle; + +struct rfid_reader { + char *name; + unsigned int id; + int (*transcieve)(struct rfid_reader_handle *h, + enum rfid_frametype frametype, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len, + u_int64_t timeout, unsigned int flags); + struct rfid_reader_handle * (*open)(void *data); + void (*close)(struct rfid_reader_handle *h); + + struct rfid_14443a_reader { + int (*init)(struct rfid_reader_handle *h); + int (*transcieve_sf)(struct rfid_reader_handle *h, + unsigned char cmd, + struct iso14443a_atqa *atqa); + int (*transcieve_acf)(struct rfid_reader_handle *h, + struct iso14443a_anticol_cmd *cmd, + unsigned int *bit_of_col); + int (*set_speed)(struct rfid_reader_handle *h, + unsigned int tx, + unsigned int speed); + unsigned int speed; + } iso14443a; + struct rfid_14443b_reader { + int (*init)(struct rfid_reader_handle *rh); + unsigned int speed; + } iso14443b; + struct rfid_15693_reader { + int (*init)(struct rfid_reader_handle *rh); + } iso15693; + struct rfid_mifare_classic_reader { + int (*setkey)(struct rfid_reader_handle *h, unsigned char *key); + int (*auth)(struct rfid_reader_handle *h, u_int8_t cmd, + u_int32_t serno, u_int8_t block); + } mifare_classic; + struct rfid_reader *next; +}; + +enum rfid_reader_id { + RFID_READER_CM5121, + RFID_READER_PEGODA, +}; + +struct rfid_reader_handle { + struct rfid_asic_handle *ah; + + union { + + } priv; + struct rfid_reader *reader; +}; + + +extern struct rfid_reader_handle * +rfid_reader_open(void *data, unsigned int id); + +extern void rfid_reader_close(struct rfid_reader_handle *rh); +#endif diff --git a/include/librfid/rfid_reader_cm5121.h b/include/librfid/rfid_reader_cm5121.h new file mode 100644 index 0000000..8d9a312 --- /dev/null +++ b/include/librfid/rfid_reader_cm5121.h @@ -0,0 +1,21 @@ +#ifndef _RFID_READER_CM5121_H +#define _RFID_READER_CM5121_H + +#define CM5121_CW_CONDUCTANCE 0x3f +#define CM5121_MOD_CONDUCTANCE 0x3f +#define CM5121_14443A_BITPHASE 0xa9 +#define CM5121_14443A_THRESHOLD 0xff + +#define CM5121_14443B_BITPHASE 0xad +#define CM5121_14443B_THRESHOLD 0xff + + +extern int +PC_to_RDR_Escape(void *handle, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len); + +extern struct rfid_reader rfid_reader_cm5121; +// extern struct rfid_asic_transport cm5121_ccid; + +#endif diff --git a/pegoda/Makefile b/pegoda/Makefile new file mode 100644 index 0000000..7310022 --- /dev/null +++ b/pegoda/Makefile @@ -0,0 +1,8 @@ + +all: pegoda + +pegoda: pegoda.o + $(CC) -lusb -o $@ $^ + +clean: + rm -f pegoda *.o diff --git a/pegoda/pegoda.c b/pegoda/pegoda.c new file mode 100644 index 0000000..2eaa61e --- /dev/null +++ b/pegoda/pegoda.c @@ -0,0 +1,292 @@ +/* + * (C) 2005 by Harald Welte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + +#include +#include "pegoda.h" + +const char * +hexdump(const void *data, unsigned int len) +{ + static char string[1024]; + unsigned char *d = (unsigned char *) data; + unsigned int i, left; + + string[0] = '\0'; + left = sizeof(string); + for (i = 0; len--; i += 3) { + if (i >= sizeof(string) -4) + break; + snprintf(string+i, 4, " %02x", *d++); + } + return string; +} + +struct pegoda_handle { + struct usb_dev_handle *handle; + unsigned char seq; + unsigned char snr[4]; +}; + + +struct usb_device *find_device(u_int16_t vendor, u_int16_t device) +{ + struct usb_bus *bus; + + for (bus = usb_get_busses(); bus; bus = bus->next) { + struct usb_device *dev; + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.idVendor == vendor && + dev->descriptor.idProduct == device) { + return dev; + } + } + } + return NULL; +} + +int pegoda_transcieve(struct pegoda_handle *ph, + u_int8_t cmd, unsigned char *tx, unsigned int tx_len, + unsigned char *rx, unsigned int *rx_len) +{ + unsigned char txbuf[256]; + unsigned char rxbuf[256]; + int rc; + unsigned int len_expected; + struct pegoda_cmd_hdr *hdr = (struct pegoda_cmd_hdr *)txbuf; + struct pegoda_cmd_hdr *rxhdr = (struct pegoda_cmd_hdr *)rxbuf; + + hdr->seq = ++(ph->seq); + hdr->cmd = cmd; + hdr->len = htons(tx_len); + memcpy(txbuf + sizeof(*hdr), tx, tx_len); + + printf("tx [%u]: %s\n", tx_len+sizeof(*hdr), + hexdump(txbuf, tx_len + sizeof(*hdr))); + rc = usb_bulk_write(ph->handle, 0x02, (char *)txbuf, + tx_len + sizeof(*hdr), 0); + if (rc < 0) + return rc; + + rc = usb_bulk_read(ph->handle, 0x81, (char *)rxbuf, sizeof(rxbuf), 0); + if (rc <= 0) + return rc; + + if (rc != 2) { + fprintf(stderr, "unexpected: received %u bytes as length?\n"); + return -EIO; + } + printf("len [%u]: %s\n", rc, hexdump(rxbuf, rc)); + + len_expected = rxbuf[0]; + + if (len_expected > sizeof(rxbuf)) + return -EIO; + + rc = usb_bulk_read(ph->handle, 0x81, (char *)rxbuf, len_expected, 0); + if (rc <= 0) + return rc; + printf("rx [%u]: %s\n", rc, hexdump(rxbuf, rc)); + + if (rc < 4) + return -EIO; + + if (rxhdr->seq != hdr->seq) + return -EIO; + + *rx_len = ntohs(rxhdr->len); + + memcpy(rx, rxbuf+sizeof(*rxhdr), rc-sizeof(*rxhdr)); + + return 0; +} + +struct pegoda_handle *pegoda_open(void) +{ + struct usb_device *pegoda; + unsigned char rbuf[16]; + unsigned int rlen = sizeof(rbuf); + struct pegoda_handle *ph; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + pegoda = find_device(USB_VENDOR_PHILIPS, USB_DEVICE_PEGODA); + + if (!pegoda) + return NULL; + + ph = malloc(sizeof(*ph)); + if (!ph) + return NULL; + memset(ph, 0, sizeof(*ph)); + + printf("found pegoda, %u configurations\n", + pegoda->descriptor.bNumConfigurations); + + printf("config 2 [nr %u] has %u interfaces\n", + pegoda->config[1].bConfigurationValue, + pegoda->config[1].bNumInterfaces); + + printf("config 2 interface 0 has %u altsettings\n", + pegoda->config[1].interface[0].num_altsetting); + + ph->handle = usb_open(pegoda); + if (!ph->handle) + goto out_free; + + if (usb_set_configuration(ph->handle, 2)) + goto out_free; + + printf("configuration 2 successfully set\n"); + + if (usb_claim_interface(ph->handle, 0)) + goto out_free; + + printf("interface 0 claimed\n"); + + if (usb_set_altinterface(ph->handle, 1)) + goto out_free; + + printf("alt setting 1 selected\n"); + + pegoda_transcieve(ph, PEGODA_CMD_PCD_CONFIG, NULL, 0, rbuf, &rlen); + + return ph; +out_free: + free(ph); + return NULL; +} + +/* Transform crypto1 key from generic 6byte into rc632 specific 12byte */ +static int +mifare_transform_key(const u_int8_t *key6, u_int8_t *key12) +{ + int i; + u_int8_t ln; + u_int8_t hn; + + for (i = 0; i < 6; i++) { + ln = key6[i] & 0x0f; + hn = key6[i] >> 4; + key12[i * 2 + 1] = (~ln << 4) | ln; + key12[i * 2] = (~hn << 4) | hn; + } + return 0; +} + +static int pegoda_auth_e2(struct pegoda_handle *ph, + u_int8_t keynr, u_int8_t sector) +{ + unsigned char buf[3]; + unsigned char rbuf[16]; + unsigned int rlen = sizeof(rbuf); + + buf[0] = 0x60; + buf[1] = keynr; /* key number */ + buf[2] = sector; /* sector */ + rlen = sizeof(rbuf); + pegoda_transcieve(ph, PEGODA_CMD_PICC_AUTH, buf, 3, rbuf, &rlen); + + /* FIXME: check response */ + + return 0; +} + +static int pegoda_auth_key(struct pegoda_handle *ph, + u_int8_t sector, const unsigned char *key6) +{ + unsigned char buf[1+4+12+1]; + unsigned char rbuf[16]; + unsigned int rlen = sizeof(rbuf); + + buf[0] = 0x60; + memcpy(buf+1, ph->snr, 4); + mifare_transform_key(key6, buf+5); + buf[17] = sector; + + pegoda_transcieve(ph, PEGODA_CMD_PICC_AUTH_KEY, buf, 18, rbuf, &rlen); + + /* FIXME: check response */ + + return 0; +} + +static int pegoda_read16(struct pegoda_handle *ph, + u_int8_t page, unsigned char *rx) +{ + int rc; + unsigned int rlen = 16; + + rc = pegoda_transcieve(ph, PEGODA_CMD_PICC_READ, + &page, 1, rx, &rlen); + if (rlen != 16) + return -EIO; + + return 0; +} + +int main(int argc, char **argv) +{ + unsigned char buf[256]; + unsigned char rbuf[256]; + unsigned int rlen = sizeof(rbuf); + struct pegoda_handle *ph; + + ph = pegoda_open(); + if (!ph) + exit(1); + + /* LED off */ + buf[0] = 0x00; + rlen = sizeof(rbuf); + pegoda_transcieve(ph, PEGODA_CMD_SWITCH_LED, buf, 1, rbuf, &rlen); + + /* anticollision */ + + buf[0] = 0x26; + rlen = sizeof(rbuf); + pegoda_transcieve(ph, PEGODA_CMD_PICC_COMMON_REQUEST, + buf, 1, rbuf, &rlen); + + buf[0] = 0x93; + memset(buf+1, 0, 5); + rlen = sizeof(rbuf); + pegoda_transcieve(ph, PEGODA_CMD_PICC_CASC_ANTICOLL, + buf, 6, rbuf, &rlen); + + memcpy(ph->snr, rbuf, 4); + + buf[0] = 0x93; + memcpy(buf+1, ph->snr, 4); + rlen = sizeof(rbuf); + pegoda_transcieve(ph, PEGODA_CMD_PICC_CASC_SELECT, + buf, 5, rbuf, &rlen); + + pegoda_auth_key(ph, 0, "\xff\xff\xff\xff\xff\xff"); + pegoda_read16(ph, 0, rbuf); + printf("read16 = %s\n", hexdump(rbuf, 16)); + + exit(0); +} diff --git a/pegoda/pegoda.h b/pegoda/pegoda.h new file mode 100644 index 0000000..236dc0a --- /dev/null +++ b/pegoda/pegoda.h @@ -0,0 +1,140 @@ +#ifndef _PEGODA_H +#define _PEGODA_H +/* + * (C) 2005 by Harald Welte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define USB_VENDOR_PHILIPS 0x0742 +#define USB_DEVICE_PEGODA 0xff01 + +/* header of a pegoda usb command packet */ +struct pegoda_cmd_hdr { + u_int8_t seq; + u_int8_t cmd; + u_int16_t len; +}; + +enum { + PEGODA_CMD_WRITE_RC = 0x01, + PEGODA_CMD_READ_RC = 0x02, + PEGODA_CMD_EXCHANGE_BYTESTREAM = 0x03, + PEGODA_CMD_WRITE_MULTIPLE = 0x04, + PEGODA_CMD_READ_MULTIPLE = 0x05, + PEGODA_CMD_READ_WORD = 0x06, + + + PEGODA_CMD_PCD_CONFIG = 0x10, + PEGODA_CMD_PICC_REQUEST = 0x11, + PEGODA_CMD_PICC_ANTICOLL = 0x12, + PEGODA_CMD_PICC_SELECT = 0x13, + PEGODA_CMD_PICC_AUTH = 0x14, + PEGODA_CMD_PICC_AUTH_E2 = 0x15, + PEGODA_CMD_LOAD_KEY_E2 = 0x17, + PEGODA_CMD_PICC_AUTH_KEY = 0x18, + PEGODA_CMD_PICC_READ = 0x19, + PEGODA_CMD_PICC_WRITE = 0x1a, + PEGODA_CMD_PICC_VALUE = 0x1b, + PEGODA_CMD_PICC_VALUE_DEBIT = 0x1c, + PEGODA_CMD_PICC_HALT = 0x1d, + PEGODA_CMD_PICC_WRITE4 = 0x1e, + PEGODA_CMD_PICC_COMMON_WRITE = 0x1f, + + PEGODA_CMD_PCD_RF_RESET = 0x20, + PEGODA_CMD_PCD_RESET = 0x21, + PEGODA_CMD_PCD_GET_SNR = 0x22, + PEGODA_CMD_PCD_READ_E2 = 0x23, + PEGODA_CMD_PCD_SET_TMO = 0x27, + PEGODA_CMD_PICC_COMMON_READ = 0x28, + PEGODA_CMD_PCD_INITIALIZE_E2 = 0x29, + PEGODA_CMD_ACTIVE_ANTENNA_MASTER = 0x2a, + PEGODA_CMD_ACTIVE_ANTENNA_SLAVE = 0x2b, + + PEGODA_CMD_HOST_TRANS_TMR_START = 0x30, + PEGODA_CMD_HOST_TRANS_TMR_STOP = 0x31, + PEGODA_CMD_HOST_GET_EXEC_TIME = 0x32, + + PEGODA_CMD_PICC_COMMON_REQUEST = 0x40, + PEGODA_CMD_PICC_CASC_ANTICOLL = 0x41, + PEGODA_CMD_PICC_CASC_SELECT = 0x42, + PEGODA_CMD_PICC_ACTIVATE_IDLE = 0x43, + PEGODA_CMD_PICC_ACTIVATE_WAKEUP = 0x44, + PEGODA_CMD_PCD_SET_DEFAULT_ATTRIB = 0x45, + PEGODA_CMD_PCD_SET_ATTRIB = 0x46, + PEGODA_CMD_PCD_GET_ATTRIB = 0x47, + PEGODA_CMD_PICC_EXCHANGE_BLOCK = 0x48, + PEGODA_CMD_PICC_ACTIVATE_IDLE_LOOP = 0x49, + PEGODA_CMD_PICC_ACTTIVATION = 0x4a, + PEGODA_CMD_PCD_ENABLE_HIGH_BAUD_RATES = 0x4b, + + PEGODA_CMD_SWITCH_LED = 0x60, + PEGODA_CMD_TEST_FLASH_NR = 0x61, + PEGODA_CMD_START_DOWNLOAD = 0x62, + PEGODA_CMD_GET_FW_VERSION = 0x63, + PEGODA_CMD_GET_RIC_VERSION = 0x64, + PEGODA_CMD_ENABLE_DEBUG_PINS = 0x65, + PEGODA_CMD_MIRROR_DATA = 0x66, + PEGODA_CMD_TEST_CMDS = 0x67, + + PEGODA_CMD_TEST_PCD = 0x70, + PEGODA_CMD_TEST_PICC = 0x71, + PEGODA_CMD_GET_ONLINE_STATUS = 0x72, + + PEGODA_CMD_I1_PCD_CONFIG = 0x80, + PEGODA_CMD_I1_INIT_STD_MODE = 0x81, + PEGODA_CMD_I1_CALC_TSE_HASH = 0x82, + PEGODA_CMD_I1_INIT_FAST_MODE = 0x83, + PEGODA_CMD_I1_OUTPUT_READ = 0x85, + PEGODA_CMD_I1_OUTPUT_ANTICOL_SELECT = 0x86, + PEGODA_CMD_I1_OUTPUT_WRITE = 0x87, + PEGODA_CMD_I1_OUTPUT_HALT = 0x88, + PEGODA_CMD_I1_OUTPUT_EAS = 0x89, + PEGODA_CMD_I1_RESET_QUIET = 0x8a, + PEGODA_CMD_I1_PCD_RF_RESET = 0x8b, + PEGODA_CMD_I1_SET_BIT_PHASE = 0x8c, + + PEGODA_CMD_I2_INIT_STD_MODE = 0x90, + PEGODA_CMD_I2_INIT_FAST_MODE = 0x91, + PEGODA_CMD_15693_READ_SM = 0x92, + PEGODA_CMD_15693_INVENTORY = 0x93, + PEGODA_CMD_15693_WRITE_SM = 0x94, + PEGODA_CMD_15693_STAY_QUIET = 0x95, + PEGODA_CMD_15693_LOCK_BLOCK = 0x96, + PEGODA_CMD_15693_SELECT = 0x97, + PEGODA_CMD_15693_RESET_TO_READY = 0x98, + PEGODA_CMD_15693_WRITE_AFI = 0x99, + PEGODA_CMD_15693_LOCK_AFI = 0x9a, + PEGODA_CMD_15693_WRITE_DSFID = 0x9b, + PEGODA_CMD_15693_LOCK_DSFID = 0x9c, + PEGODA_CMD_15693_GET_SYSTEM_INFO = 0x9d, + PEGODA_CMD_15693_GET_MULTI_BLOCK_SEC = 0x9e, + PEGODA_CMD_15693_INVENTORY_READ = 0x9f, + PEGODA_CMD_15693_FAST_INVENTORY_READ = 0xa0, + PEGODA_CMD_15693_SET_EAS = 0xa1, + PEGODA_CMD_15693_RESET_EAS = 0xa2, + PEGODA_CMD_15693_LOCK_EAS = 0xa3, + PEGODA_CMD_15693_EAS_ALARM = 0xa4, + + PEGODA_CMD_PC_PCD_CONFIG = 0xb0, + PEGODA_CMD_PC_BEGIN_ROUND = 0xb1, + PEGODA_CMD_PC_WRITE = 0xb3, + + PEGODA_CMD_UID_PCD_CONFIG = 0xb8, + PEGODA_CMD_UID_BEGIN_ROUND = 0xb9, + PEGODA_CMD_UID_WRITE = 0xba, + PEGODA_CMD_UID_DESTROY = 0xbb, +}; + +#endif diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..dbc608f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,13 @@ +LIBVERSION= 0:0:0 +INCLUDES = $(all_includes) -I$(top_srcdir)/include -D__LIBRFID__ + +lib_LTLIBRARIES = librfid.la + +CORE=rfid.c rfid_layer2.c rfid_protocol.c rfid_reader.c +L2=rfid_layer2_iso14443a.c rfid_layer2_iso14443b.c rfid_layer2_iso15693.c rfid_iso14443_common.c +PROTO=rfid_proto_tcl.c rfid_proto_mifare_ul.c rfid_proto_mifare_classic.c +READER=rfid_reader_cm5121.c rfid_reader_cm5121_openct.c rfid_asic_rc632.c + +librfid_la_LDFLAGS = -Wc,-nostartfiles -version-info $(LIBVERSION) -lopenct +librfid_la_SOURCES = $(CORE) $(L2) $(PROTO) $(READER) + diff --git a/src/rc632.h b/src/rc632.h new file mode 100644 index 0000000..977b0ee --- /dev/null +++ b/src/rc632.h @@ -0,0 +1,211 @@ +/* Register definitions for Philips CL RC632 RFID Reader IC + * + * (C) 2005 Harald Welte + * + * Licensed under GNU General Public License, Version 2 + */ + +enum rc632_registers { + RC632_REG_PAGE0 = 0x00, + RC632_REG_COMMAND = 0x01, + RC632_REG_FIFO_DATA = 0x02, + RC632_REG_PRIMARY_STATUS = 0x03, + RC632_REG_FIFO_LENGTH = 0x04, + RC632_REG_SECONDARY_STATUS = 0x05, + RC632_REG_INTERRUPT_EN = 0x06, + RC632_REG_INTERRUPT_RQ = 0x07, + + RC632_REG_PAGE1 = 0x08, + RC632_REG_CONTROL = 0x09, + RC632_REG_ERROR_FLAG = 0x0a, + RC632_REG_COLL_POS = 0x0b, + RC632_REG_TIMER_VALUE = 0x0c, + RC632_REG_CRC_RESULT_LSB = 0x0d, + RC632_REG_CRC_RESULT_MSB = 0x0e, + RC632_REG_BIT_FRAMING = 0x0f, + + RC632_REG_PAGE2 = 0x10, + RC632_REG_TX_CONTROL = 0x11, + RC632_REG_CW_CONDUCTANCE = 0x12, + RC632_REG_MOD_CONDUCTANCE = 0x13, + RC632_REG_CODER_CONTROL = 0x14, + RC632_REG_MOD_WIDTH = 0x15, + RC632_REG_MOD_WIDTH_SOF = 0x16, + RC632_REG_TYPE_B_FRAMING = 0x17, + + RC632_REG_PAGE3 = 0x18, + RC632_REG_RX_CONTROL1 = 0x19, + RC632_REG_DECODER_CONTROL = 0x1a, + RC632_REG_BIT_PHASE = 0x1b, + RC632_REG_RX_THRESHOLD = 0x1c, + RC632_REG_BPSK_DEM_CONTROL = 0x1d, + RC632_REG_RX_CONTROL2 = 0x1e, + RC632_REG_CLOCK_Q_CONTROL = 0x1f, + + RC632_REG_PAGE4 = 0x20, + RC632_REG_RX_WAIT = 0x21, + RC632_REG_CHANNEL_REDUNDANCY = 0x22, + RC632_REG_CRC_PRESET_LSB = 0x23, + RC632_REG_CRC_PRESET_MSB = 0x24, + RC632_REG_TIME_SLOT_PERIOD = 0x25, + RC632_REG_MFOUT_SELECT = 0x26, + RC632_REG_PRESET_27 = 0x27, + + RC632_REG_PAGE5 = 0x28, + RC632_REG_FIFO_LEVEL = 0x29, + RC632_REG_TIMER_CLOCK = 0x2a, + RC632_REG_TIMER_CONTROL = 0x2b, + RC632_REG_TIMER_RELOAD = 0x2c, + RC632_REG_IRQ_PIN_CONFIG = 0x2d, + RC632_REG_PRESET_2E = 0x2e, + RC632_REG_PRESET_2F = 0x2f, + + RC632_REG_PAGE6 = 0x30, + + RC632_REG_PAGE7 = 0x38, + RC632_REG_TEST_ANA_SELECT = 0x3a, + RC632_REG_TEST_DIGI_SELECT = 0x3d, +}; + +enum rc632_reg_command { + RC632_CMD_IDLE = 0x00, + RC632_CMD_WRITE_E2 = 0x01, + RC632_CMD_READ_E2 = 0x03, + RC632_CMD_LOAD_CONFIG = 0x07, + RC632_CMD_LOAD_KEY_E2 = 0x0b, + RC632_CMD_AUTHENT1 = 0x0c, + RC632_CMD_CALC_CRC = 0x12, + RC632_CMD_AUTHENT2 = 0x14, + RC632_CMD_RECEIVE = 0x16, + RC632_CMD_LOAD_KEY = 0x19, + RC632_CMD_TRANSMIT = 0x1a, + RC632_CMD_TRANSCIEVE = 0x1e, + RC632_CMD_STARTUP = 0x3f, +}; + +enum rc632_reg_control { + RC632_CONTROL_CRYPTO1_ON = 0x08, + RC632_CONTROL_POWERDOWN = 0x10, +}; + +enum rc632_reg_error_flag { + RC632_ERR_FLAG_COL_ERR = 0x01, + RC632_ERR_FLAG_PARITY_ERR = 0x02, + RC632_ERR_FLAG_FRAMING_ERR = 0x04, + RC632_ERR_FLAG_CRC_ERR = 0x08, + RC632_ERR_FLAG_FIFO_OVERFLOW = 0x10, + RC632_ERR_FLAG_ACCESS_ERR = 0x20, + RC632_ERR_FLAG_KEY_ERR = 0x40, +}; + +enum rc632_reg_tx_control { + RC632_TXCTRL_TX1_RF_EN = 0x01, + RC632_TXCTRL_TX2_RF_EN = 0x02, + RC632_TXCTRL_TX2_CW = 0x04, + RC632_TXCTRL_TX2_INV = 0x08, + RC632_TXCTRL_FORCE_100_ASK = 0x10, + + RC632_TXCTRL_MOD_SRC_LOW = 0x00, + RC632_TXCTRL_MOD_SRC_HIGH = 0x20, + RC632_TXCTRL_MOD_SRC_INT = 0x40, + RC632_TXCTRL_MOD_SRC_MFIN = 0x60, +}; + +enum rc632_reg_coder_control { + RC632_CDRCTRL_TXCD_NRZ = 0x00, + RC632_CDRCTRL_TXCD_14443A = 0x01, + RC632_CDRCTRL_TXCD_ICODE_STD = 0x04, + +#define RC632_CDRCTRL_RATE_MASK 0x38 + RC632_CDRCTRL_RATE_848K = 0x00, + RC632_CDRCTRL_RATE_424K = 0x08, + RC632_CDRCTRL_RATE_212K = 0x10, + RC632_CDRCTRL_RATE_106K = 0x18, + RC632_CDRCTRL_RATE_14443B = 0x20, + RC632_CDRCTRL_RATE_15693 = 0x28, + RC632_CDRCTRL_RATE_ICODE_FAST = 0x30, +}; + +enum rc632_erg_type_b_framing { + RC632_TBFRAMING_SOF_10L_2H = 0x00, + RC632_TBFRAMING_SOF_10L_3H = 0x01, + RC632_TBFRAMING_SOF_11L_2H = 0x02, + RC632_TBFRAMING_SOF_11L_3H = 0x03, + + RC632_TBFRAMING_EOF_10 = 0x00, + RC632_TBFRAMING_EOF_11 = 0x20, + + RC632_TBFRAMING_NO_TX_SOF = 0x80, + RC632_TBFRAMING_NO_TX_EOF = 0x40, +}; +#define RC632_TBFRAMING_SPACE_SHIFT 2 +#define RC632_TBFRAMING_SPACE_MASK 7 + +enum rc632_reg_rx_control1 { + RC632_RXCTRL1_GAIN_20DB = 0x00, + RC632_RXCTRL1_GAIN_24DB = 0x01, + RC632_RXCTRL1_GAIN_31DB = 0x02, + RC632_RXCTRL1_GAIN_35DB = 0x03, + + RC632_RXCTRL1_LP_OFF = 0x04, + RC632_RXCTRL1_ISO15693 = 0x08, + RC632_RXCTRL1_ISO14443 = 0x10, + +#define RC632_RXCTRL1_SUBCP_MASK 0xe0 + RC632_RXCTRL1_SUBCP_1 = 0x00, + RC632_RXCTRL1_SUBCP_2 = 0x20, + RC632_RXCTRL1_SUBCP_4 = 0x40, + RC632_RXCTRL1_SUBCP_8 = 0x60, + RC632_RXCTRL1_SUBCP_16 = 0x80, +}; + +enum rc632_reg_decoder_control { + RC632_DECCTRL_MANCHESTER = 0x00, + RC632_DECCTRL_BPSK = 0x01, + + RC632_DECCTRL_RX_INVERT = 0x04, + + RC632_DECCTRL_RXFR_ICODE = 0x00, + RC632_DECCTRL_RXFR_14443A = 0x08, + RC632_DECCTRL_RXFR_15693 = 0x10, + RC632_DECCTRL_RXFR_14443B = 0x18, + + RC632_DECCTRL_ZEROAFTERCOL = 0x20, + + RC632_DECCTRL_RX_MULTIPLE = 0x40, +}; + +enum rc632_reg_bpsk_dem_control { + RC632_BPSKD_TAUB_SHIFT = 0x00, + RC632_BPSKD_TAUB_MASK = 0x03, + + RC632_BPSKD_TAUD_SHIFT = 0x02, + RC632_BPSKD_TAUD_MASK = 0x03, + + RC632_BPSKD_FILTER_AMP_DETECT = 0x10, + RC632_BPSKD_NO_RX_EOF = 0x20, + RC632_BPSKD_NO_RX_EGT = 0x40, + RC632_BPSKD_NO_RX_SOF = 0x80, +}; + +enum rc632_reg_rx_control2 { + RC632_RXCTRL2_DECSRC_LOW = 0x00, + RC632_RXCTRL2_DECSRC_INT = 0x01, + RC632_RXCTRL2_DECSRC_SUBC_MFIN = 0x10, + RC632_RXCTRL2_DECSRC_BASE_MFIN = 0x11, + + RC632_RXCTRL2_AUTO_PD = 0x40, + RC632_RXCTRL2_CLK_I = 0x80, + RC632_RXCTRL2_CLK_Q = 0x00, +}; + +enum rc632_reg_channel_redundancy { + RC632_CR_PARITY_ENABLE = 0x01, + RC632_CR_PARITY_ODD = 0x02, + RC632_CR_TX_CRC_ENABLE = 0x04, + RC632_CR_RX_CRC_ENABLE = 0x08, + RC632_CR_CRC8 = 0x10, + RC632_CR_CRC3309 = 0x20, +}; + + diff --git a/src/rfid.c b/src/rfid.c new file mode 100644 index 0000000..6fba46a --- /dev/null +++ b/src/rfid.c @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include + +#include +#include +#include +#include +#include + +const char * +rfid_hexdump(const void *data, unsigned int len) +{ + static char string[1024]; + unsigned char *d = (unsigned char *) data; + unsigned int i, left; + + string[0] = '\0'; + left = sizeof(string); + for (i = 0; len--; i += 3) { + if (i >= sizeof(string) -4) + break; + snprintf(string+i, 4, " %02x", *d++); + } + return string; +} + +int rfid_init() +{ + rfid_reader_register(&rfid_reader_cm5121); + rfid_layer2_register(&rfid_layer2_iso14443a); + rfid_layer2_register(&rfid_layer2_iso14443b); + rfid_protocol_register(&rfid_protocol_tcl); + rfid_protocol_register(&rfid_protocol_mful); + rfid_protocol_register(&rfid_protocol_mfcl); + + return 0; +} + +void rfid_fini() +{ + /* FIXME: implementation */ +} diff --git a/src/rfid_asic_rc632.c b/src/rfid_asic_rc632.c new file mode 100644 index 0000000..0c38052 --- /dev/null +++ b/src/rfid_asic_rc632.c @@ -0,0 +1,1435 @@ +/* Generic Philips CL RC632 Routines + * + * (C) Harald Welte + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "rfid_iso14443_common.h" +#include "rc632.h" +//#include "rc632_14443a.h" + + +#define RC632_TMO_AUTH1 14000 + +#define ENTER() DEBUGP("entering\n") +struct rfid_asic rc632; + +/* Register and FIFO Access functions */ +static int +rc632_reg_write(struct rfid_asic_handle *handle, + u_int8_t reg, + u_int8_t val) +{ + return handle->rath->rat->priv.rc632.fn.reg_write(handle->rath, reg, val); +} + +static int +rc632_reg_read(struct rfid_asic_handle *handle, + u_int8_t reg, + u_int8_t *val) +{ + return handle->rath->rat->priv.rc632.fn.reg_read(handle->rath, reg, val); +} + +static int +rc632_fifo_write(struct rfid_asic_handle *handle, + u_int8_t len, + const u_int8_t *buf, + u_int8_t flags) +{ + return handle->rath->rat->priv.rc632.fn.fifo_write(handle->rath, + len, buf, flags); +} + +static int +rc632_fifo_read(struct rfid_asic_handle *handle, + u_int8_t len, + u_int8_t *buf) +{ + return handle->rath->rat->priv.rc632.fn.fifo_read(handle->rath, len, buf); +} + + +static int +rc632_set_bits(struct rfid_asic_handle *handle, + u_int8_t reg, + u_int8_t val) +{ + int ret; + u_int8_t tmp; + + ret = rc632_reg_read(handle, reg, &tmp); + if (ret < 0) + return -1; + + /* if bits are already set, no need to set them again */ + if ((tmp & val) == val) + return 0; + + return rc632_reg_write(handle, reg, (tmp|val)&0xff); +} +static int +rc632_set_bit_mask(struct rfid_asic_handle *handle, + u_int8_t reg, u_int8_t mask, u_int8_t val) +{ + int ret; + u_int8_t tmp; + + ret = rc632_reg_read(handle, reg, &tmp); + if (ret < 0) + return ret; + + /* if bits are already like we want them, abort */ + if ((tmp & mask) == val) + return 0; + + return rc632_reg_write(handle, reg, (tmp & ~mask)|(val & mask)); +} + +static int +rc632_clear_bits(struct rfid_asic_handle *handle, + u_int8_t reg, + u_int8_t val) +{ + int ret; + u_int8_t tmp; + + ret = rc632_reg_read(handle, reg, &tmp); + if (ret < 0) { + DEBUGP("error during reg_read(%p, %d):%d\n", + handle, reg, ret); + return -1; + } + /* if bits are already cleared, no need to clear them again */ + if ((tmp & val) == 0) + return 0; + + return rc632_reg_write(handle, reg, (tmp & ~val)&0xff); +} + +static int +rc632_turn_on_rf(struct rfid_asic_handle *handle) +{ + ENTER(); + return rc632_set_bits(handle, RC632_REG_TX_CONTROL, 0x03); +} + +static int +rc632_turn_off_rf(struct rfid_asic_handle *handle) +{ + ENTER(); + return rc632_clear_bits(handle, RC632_REG_TX_CONTROL, 0x03); +} + +static int +rc632_power_up(struct rfid_asic_handle *handle) +{ + ENTER(); + return rc632_clear_bits(handle, RC632_REG_CONTROL, + RC632_CONTROL_POWERDOWN); +} + +static int +rc632_power_down(struct rfid_asic_handle *handle) +{ + return rc632_set_bits(handle, RC632_REG_CONTROL, + RC632_CONTROL_POWERDOWN); +} + +/* Stupid RC623 implementations don't evaluate interrupts but poll the + * command register for "status idle" */ +static int +rc632_wait_idle(struct rfid_asic_handle *handle, u_int64_t timeout) +{ + u_int8_t cmd = 0xff; + int ret; + + while (cmd != 0) { + ret = rc632_reg_read(handle, RC632_REG_COMMAND, &cmd); + if (ret < 0) + return ret; + + if (cmd == 0) { + /* FIXME: read second time ?? */ + return 0; + } + + { + u_int8_t foo; + rc632_reg_read(handle, RC632_REG_PRIMARY_STATUS, &foo); + if (foo & 0x04) + rc632_reg_read(handle, RC632_REG_ERROR_FLAG, &foo); + } + + usleep(100); + + /* Fixme: Abort after some timeout */ + } + + return 0; +} + +static int +rc632_transmit(struct rfid_asic_handle *handle, + const u_int8_t *buf, + u_int8_t len, + u_int64_t timeout) +{ + int ret; + + ret = rc632_fifo_write(handle, len, buf, 0x03); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_COMMAND, RC632_CMD_TRANSMIT); + if (ret < 0) + return ret; + + return rc632_wait_idle(handle, timeout); +} + +static int +tcl_toggle_pcb(struct rfid_asic_handle *handle) +{ + // FIXME: toggle something between 0x0a and 0x0b + return 0; +} + +static int +rc632_transcieve(struct rfid_asic_handle *handle, + const u_int8_t *tx_buf, + u_int8_t tx_len, + u_int8_t *rx_buf, + u_int8_t *rx_len, + unsigned int timer, + unsigned int toggle) +{ + int ret; + + ret = rc632_fifo_write(handle, tx_len, tx_buf, 0x03); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_COMMAND, RC632_CMD_TRANSCIEVE); + if (ret < 0) + return ret; + + if (toggle == 1) + tcl_toggle_pcb(handle); + + ret = rc632_wait_idle(handle, timer); + if (ret < 0) + return ret; + + ret = rc632_reg_read(handle, RC632_REG_FIFO_LENGTH, rx_len); + if (ret < 0) + return ret; + + if (*rx_len == 0) { + u_int8_t tmp; + + DEBUGP("rx_len == 0\n"); + + rc632_reg_read(handle, RC632_REG_ERROR_FLAG, &tmp); + rc632_reg_read(handle, RC632_REG_CHANNEL_REDUNDANCY, &tmp); + + return -1; + } + + return rc632_fifo_read(handle, *rx_len, rx_buf); +} + +static int +rc632_read_eeprom(struct rfid_asic_handle *handle) +{ + u_int8_t recvbuf[60]; + u_int8_t sndbuf[3]; + int ret; + + sndbuf[0] = 0x00; + sndbuf[1] = 0x00; + sndbuf[2] = 0x3c; + + ret = rc632_fifo_write(handle, 3, sndbuf, 0x03); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_COMMAND, RC632_CMD_READ_E2); + if (ret < 0) + return ret; + + usleep(20000); + + ret = rc632_fifo_read(handle, sizeof(recvbuf), recvbuf); + if (ret < 0) + return ret; + + // FIXME: do something with eeprom contents + return ret; +} + +static int +rc632_calc_crc16_from(struct rfid_asic_handle *handle) +{ + u_int8_t sndbuf[2] = { 0x01, 0x02 }; + u_int8_t crc_lsb = 0x00 , crc_msb = 0x00; + int ret; + + ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_LSB, 0x12); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_MSB, 0xe0); + if (ret < 0) + return ret; + + ret = rc632_fifo_write(handle, sizeof(sndbuf), sndbuf, 3); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_COMMAND, RC632_CMD_CALC_CRC); + if (ret < 0) + return ret; + + usleep(10000); // FIXME: no checking for cmd completion? + + ret = rc632_reg_read(handle, RC632_REG_CRC_RESULT_LSB, &crc_lsb); + if (ret < 0) + return ret; + + ret = rc632_reg_read(handle, RC632_REG_CRC_RESULT_MSB, &crc_msb); + if (ret < 0) + return ret; + + // FIXME: what to do with crc result? + return ret; +} + + +int +rc632_register_dump(struct rfid_asic_handle *handle, u_int8_t *buf) +{ + int ret; + u_int8_t i; + + for (i = 0; i <= 0x3f; i++) { + ret = rc632_reg_read(handle, i, &buf[i]); + // do we want error checks? + } + return 0; +} + + + +/* generic FIFO access functions (if no more efficient ones provided by + * transport driver) */ + +static int +generic_fifo_write() +{ + // FIXME: implementation (not needed for CM 5121) + return -1; +} + +static int +generic_fifo_read() +{ + // FIXME: implementation (not neded for CM 5121) + return -1; +} + +static int +rc632_init(struct rfid_asic_handle *ah) +{ + int ret; + + /* switch off rf (make sure PICCs are reset at init time) */ + ret = rc632_power_down(ah); + if (ret < 0) + return ret; + + usleep(10000); + + /* switch on rf */ + ret = rc632_power_up(ah); + if (ret < 0) + return ret; + + /* disable register paging */ + ret = rc632_reg_write(ah, 0x00, 0x00); + if (ret < 0) + return ret; + + /* set some sane default values */ + ret = rc632_reg_write(ah, 0x11, 0x5b); + if (ret < 0) + return ret; + + /* switch on rf */ + ret = rc632_turn_on_rf(ah); + if (ret < 0) + return ret; + + return 0; +} + +static int +rc632_fini(struct rfid_asic_handle *ah) +{ + int ret; + + /* switch off rf */ + ret = rc632_turn_off_rf(ah); + if (ret < 0) + return ret; + + ret = rc632_power_down(ah); + if (ret < 0) + return ret; + + return 0; +} + +struct rfid_asic_handle * +rc632_open(struct rfid_asic_transport_handle *th) +{ + struct rfid_asic_handle *h; + + h = malloc(sizeof(*h)); + if (!h) + return NULL; + memset(h, 0, sizeof(*h)); + + h->asic = &rc632; + h->rath = th; + h->fc = h->asic->fc; + h->mtu = h->mru = 40; /* FIXME */ + + if (rc632_init(h) < 0) { + free(h); + return NULL; + } + + return h; +} + +void +rc632_close(struct rfid_asic_handle *h) +{ + rc632_fini(h); + free(h); +} + + +/* + * Philips CL RC632 primitives for ISO 14443-A compliant PICC's + * + * (C) 2005 by Harald Welte + * + */ + +static int +rc632_iso14443a_init(struct rfid_asic_handle *handle) +{ + int ret; + + // FIXME: some fifo work (drain fifo?) + + /* flush fifo (our way) */ + ret = rc632_reg_write(handle, RC632_REG_CONTROL, 0x01); + + ret = rc632_reg_write(handle, RC632_REG_TX_CONTROL, + (RC632_TXCTRL_TX1_RF_EN | + RC632_TXCTRL_TX2_RF_EN | + RC632_TXCTRL_TX2_INV | + RC632_TXCTRL_FORCE_100_ASK | + RC632_TXCTRL_MOD_SRC_INT)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_CW_CONDUCTANCE, + CM5121_CW_CONDUCTANCE); + if (ret < 0) + return ret; + + /* Since FORCE_100_ASK is set (cf mc073930.pdf), this line may be left out? */ + ret = rc632_reg_write(handle, RC632_REG_MOD_CONDUCTANCE, + CM5121_MOD_CONDUCTANCE); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_CODER_CONTROL, + (RC632_CDRCTRL_TXCD_14443A | + RC632_CDRCTRL_RATE_106K)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_MOD_WIDTH, 0x13); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_MOD_WIDTH_SOF, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_TYPE_B_FRAMING, 0x00); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_RX_CONTROL1, + (RC632_RXCTRL1_GAIN_35DB | + RC632_RXCTRL1_ISO14443 | + RC632_RXCTRL1_SUBCP_8)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_DECODER_CONTROL, + (RC632_DECCTRL_MANCHESTER | + RC632_DECCTRL_RXFR_14443A)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_BIT_PHASE, + CM5121_14443A_BITPHASE); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_RX_THRESHOLD, + CM5121_14443A_THRESHOLD); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_BPSK_DEM_CONTROL, 0x00); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_RX_CONTROL2, + (RC632_RXCTRL2_DECSRC_INT | + RC632_RXCTRL2_CLK_Q)); + if (ret < 0) + return ret; + + /* Omnikey proprietary driver has 0x03, but 0x06 is the default reset value ?!? */ + ret = rc632_reg_write(handle, RC632_REG_RX_WAIT, 0x06); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY, + (RC632_CR_PARITY_ENABLE | + RC632_CR_PARITY_ODD)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_LSB, 0x63); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_CRC_PRESET_MSB, 0x63); + if (ret < 0) + return ret; + + return 0; +} + +static int +rc632_iso14443a_fini(struct iso14443a_handle *handle_14443) +{ + +#if 0 + ret = rc632_turn_off_rf(handle); + if (ret < 0) + return ret; +#endif + + + return 0; +} + + +/* issue a 14443-3 A PCD -> PICC command in a short frame, such as REQA, WUPA */ +static int +rc632_iso14443a_transcieve_sf(struct rfid_asic_handle *handle, + u_int8_t cmd, + struct iso14443a_atqa *atqa) +{ + int ret; + u_int8_t tx_buf[1]; + u_int8_t rx_len = 2; + + memset(atqa, 0, sizeof(atqa)); + + tx_buf[0] = cmd; + + /* transfer only 7 bits of last byte in frame */ + ret = rc632_reg_write(handle, RC632_REG_BIT_FRAMING, 0x07); + if (ret < 0) + return ret; + + ret = rc632_clear_bits(handle, RC632_REG_CONTROL, + RC632_CONTROL_CRYPTO1_ON); + if (ret < 0) + return ret; + +#if 0 + ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY, + (RC632_CR_PARITY_ENABLE | + RC632_CR_PARITY_ODD)); +#else + ret = rc632_clear_bits(handle, RC632_REG_CHANNEL_REDUNDANCY, + RC632_CR_RX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE); + +#endif + if (ret < 0) + return ret; + + ret = rc632_transcieve(handle, tx_buf, sizeof(tx_buf), + (u_int8_t *)atqa, &rx_len, 0x32, 0); + if (ret < 0) { + DEBUGP("error during rc632_transcieve()\n"); + return ret; + } + + /* switch back to normal 8bit last byte */ + ret = rc632_reg_write(handle, RC632_REG_BIT_FRAMING, 0x00); + if (ret < 0) + return ret; + + if (rx_len != 2) { + DEBUGP("rx_len(%d) != 2\n", rx_len); + return -1; + } + + return 0; +} + +/* transcieve regular frame */ +static int +rc632_iso14443ab_transcieve(struct rfid_asic_handle *handle, + unsigned int frametype, + const u_int8_t *tx_buf, unsigned int tx_len, + u_int8_t *rx_buf, unsigned int *rx_len, + u_int64_t timeout, unsigned int flags) +{ + int ret; + u_int8_t rxl = *rx_len & 0xff; + u_int8_t channel_red; + + memset(rx_buf, 0, *rx_len); + + switch (frametype) { + case RFID_14443A_FRAME_REGULAR: + case RFID_MIFARE_FRAME: + channel_red = RC632_CR_RX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE + |RC632_CR_PARITY_ENABLE|RC632_CR_PARITY_ODD; + break; + case RFID_14443B_FRAME_REGULAR: + channel_red = RC632_CR_RX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE + |RC632_CR_CRC3309; + break; +#if 0 + case RFID_MIFARE_FRAME: + channel_red = RC632_CR_PARITY_ENABLE|RC632_CR_PARITY_ODD; + break; +#endif + default: + return -EINVAL; + break; + } + ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY, + channel_red); + if (ret < 0) + return ret; + + ret = rc632_transcieve(handle, tx_buf, tx_len, rx_buf, &rxl, 0x32, 0); + *rx_len = rxl; + if (ret < 0) + return ret; + + + return 0; +} + +/* transcieve anti collission bitframe */ +static int +rc632_iso14443a_transcieve_acf(struct rfid_asic_handle *handle, + struct iso14443a_anticol_cmd *acf, + unsigned int *bit_of_col) +{ + int ret; + u_int8_t rx_buf[64]; + u_int8_t rx_len = sizeof(rx_buf); + u_int8_t rx_align = 0, tx_last_bits, tx_bytes; + u_int8_t boc; + u_int8_t error_flag; + *bit_of_col = ISO14443A_BITOFCOL_NONE; + memset(rx_buf, 0, sizeof(rx_buf)); + + /* disable mifare cryto */ + ret = rc632_clear_bits(handle, RC632_REG_CONTROL, + RC632_CONTROL_CRYPTO1_ON); + if (ret < 0) + return ret; + + /* disable CRC summing */ +#if 0 + ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY, + (RC632_CR_PARITY_ENABLE | + RC632_CR_PARITY_ODD)); +#else + ret = rc632_clear_bits(handle, RC632_REG_CHANNEL_REDUNDANCY, + RC632_CR_TX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE); +#endif + if (ret < 0) + return ret; + + tx_last_bits = acf->nvb & 0x0f; /* lower nibble indicates bits */ + tx_bytes = acf->nvb >> 4; + if (tx_last_bits) { + tx_bytes++; + rx_align = (tx_last_bits+1) % 8;/* rx frame complements tx */ + } + + //rx_align = 8 - tx_last_bits;/* rx frame complements tx */ + + /* set RxAlign and TxLastBits*/ + ret = rc632_reg_write(handle, RC632_REG_BIT_FRAMING, + (rx_align << 4) | (tx_last_bits)); + if (ret < 0) + return ret; + + ret = rc632_transcieve(handle, (u_int8_t *)acf, tx_bytes, + rx_buf, &rx_len, 0x32, 0); + if (ret < 0) + return ret; + + /* bitwise-OR the two halves of the split byte */ + acf->uid_bits[tx_bytes-2] = ( + (acf->uid_bits[tx_bytes-2] & (0xff >> (8-tx_last_bits))) + | rx_buf[0]); + /* copy the rest */ + memcpy(&acf->uid_bits[tx_bytes+1-2], &rx_buf[1], rx_len-1); + + /* determine whether there was a collission */ + ret = rc632_reg_read(handle, RC632_REG_ERROR_FLAG, &error_flag); + if (ret < 0) + return ret; + + if (error_flag & RC632_ERR_FLAG_COL_ERR) { + /* retrieve bit of collission */ + ret = rc632_reg_read(handle, RC632_REG_COLL_POS, &boc); + if (ret < 0) + return ret; + + /* bit of collission relative to start of part 1 of + * anticollision frame (!) */ + *bit_of_col = 2*8 + boc; + } + + return 0; +} + +enum rc632_rate { + RC632_RATE_106 = 0x00, + RC632_RATE_212 = 0x01, + RC632_RATE_424 = 0x02, + RC632_RATE_848 = 0x03, +}; + +struct rx_config { + u_int8_t subc_pulses; + u_int8_t rx_coding; + u_int8_t rx_threshold; + u_int8_t bpsk_dem_ctrl; +}; + +struct tx_config { + u_int8_t rate; + u_int8_t mod_width; +}; + +static struct rx_config rx_configs[] = { + { + .subc_pulses = RC632_RXCTRL1_SUBCP_8, + .rx_coding = RC632_DECCTRL_MANCHESTER, + .rx_threshold = 0x88, + .bpsk_dem_ctrl = 0x00, + }, + { + .subc_pulses = RC632_RXCTRL1_SUBCP_4, + .rx_coding = RC632_DECCTRL_BPSK, + .rx_threshold = 0x50, + .bpsk_dem_ctrl = 0x0c, + }, + { + .subc_pulses = RC632_RXCTRL1_SUBCP_2, + .rx_coding = RC632_DECCTRL_BPSK, + .rx_threshold = 0x50, + .bpsk_dem_ctrl = 0x0c, + }, + { + .subc_pulses = RC632_RXCTRL1_SUBCP_1, + .rx_coding = RC632_DECCTRL_BPSK, + .rx_threshold = 0x50, + .bpsk_dem_ctrl = 0x0c, + }, +}; + +static struct tx_config tx_configs[] = { + { + .rate = RC632_CDRCTRL_RATE_106K, + .mod_width = 0x13, + }, + { + .rate = RC632_CDRCTRL_RATE_212K, + .mod_width = 0x07, + }, + { + .rate = RC632_CDRCTRL_RATE_424K, + .mod_width = 0x03, + }, + { + .rate = RC632_CDRCTRL_RATE_848K, + .mod_width = 0x01, + }, +}; + +static int rc632_iso14443a_set_speed(struct rfid_asic_handle *handle, + unsigned int tx, + u_int8_t rate) +{ + int rc; + u_int8_t reg; + + + if (!tx) { + /* Rx */ + if (rate > ARRAY_SIZE(rx_configs)) + return -EINVAL; + + rc = rc632_set_bit_mask(handle, RC632_REG_RX_CONTROL1, + RC632_RXCTRL1_SUBCP_MASK, + rx_configs[rate].subc_pulses); + if (rc < 0) + return rc; + + rc = rc632_set_bit_mask(handle, RC632_REG_DECODER_CONTROL, + RC632_DECCTRL_BPSK, + rx_configs[rate].rx_coding); + if (rc < 0) + return rc; + + rc = rc632_reg_write(handle, RC632_REG_RX_THRESHOLD, + rx_configs[rate].rx_threshold); + if (rc < 0) + return rc; + + if (rx_configs[rate].rx_coding == RC632_DECCTRL_BPSK) { + rc = rc632_reg_write(handle, + RC632_REG_BPSK_DEM_CONTROL, + rx_configs[rate].bpsk_dem_ctrl); + if (rc < 0) + return rc; + } + } else { + /* Tx */ + if (rate > ARRAY_SIZE(tx_configs)) + return -EINVAL; + + rc = rc632_set_bit_mask(handle, RC632_REG_CODER_CONTROL, + RC632_CDRCTRL_RATE_MASK, + tx_configs[rate].rate); + if (rc < 0) + return rc; + + rc = rc632_reg_write(handle, RC632_REG_MOD_WIDTH, + tx_configs[rate].mod_width); + if (rc < 0) + return rc; + } + + return 0; +} + +static int rc632_iso14443b_init(struct rfid_asic_handle *handle) +{ + int ret; + + // FIXME: some FIFO work + + /* flush fifo (our way) */ + ret = rc632_reg_write(handle, RC632_REG_CONTROL, 0x01); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_TX_CONTROL, + (RC632_TXCTRL_TX1_RF_EN | + RC632_TXCTRL_TX2_RF_EN | + RC632_TXCTRL_TX2_INV | + RC632_TXCTRL_MOD_SRC_INT)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_CW_CONDUCTANCE, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_MOD_CONDUCTANCE, 0x04); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_CODER_CONTROL, + (RC632_CDRCTRL_TXCD_NRZ | + RC632_CDRCTRL_RATE_14443B)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_MOD_WIDTH, 0x13); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_MOD_WIDTH_SOF, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_TYPE_B_FRAMING, + (RC632_TBFRAMING_SOF_11L_3H | + (6 << RC632_TBFRAMING_SPACE_SHIFT) | + RC632_TBFRAMING_EOF_11)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_RX_CONTROL1, + (RC632_RXCTRL1_GAIN_35DB | + RC632_RXCTRL1_ISO14443 | + RC632_RXCTRL1_SUBCP_8)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_DECODER_CONTROL, + (RC632_DECCTRL_BPSK | + RC632_DECCTRL_RXFR_14443B)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_BIT_PHASE, + CM5121_14443B_BITPHASE); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_RX_THRESHOLD, + CM5121_14443B_THRESHOLD); + if (ret < 0) + return ret; + + ret = rc632_reg_write(handle, RC632_REG_BPSK_DEM_CONTROL, + ((0x2 & RC632_BPSKD_TAUB_MASK)<> 4; + key12[i * 2 + 1] = (~ln << 4) | ln; + key12[i * 2] = (~hn << 4) | hn; + } + return 0; +} + +static int +rc632_mifare_set_key(struct rfid_asic_handle *h, const u_int8_t *key) +{ + u_int8_t coded_key[RFID_MIFARE_KEY_CODED_LEN]; + u_int8_t reg; + int ret; + + ret = rc632_mifare_transform_key(key, coded_key); + if (ret < 0) + return ret; + + ret = rc632_fifo_write(h, RFID_MIFARE_KEY_CODED_LEN, coded_key, 0x03); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_COMMAND, RC632_CMD_LOAD_KEY); + if (ret < 0) + return ret; + + ret = rc632_wait_idle(h, RC632_TMO_AUTH1); + if (ret < 0) + return ret; + + ret = rc632_reg_read(h, RC632_REG_ERROR_FLAG, ®); + if (ret < 0) + return ret; + + if (reg & RC632_ERR_FLAG_KEY_ERR) + return -EINVAL; + + return 0; +} + +static int +rc632_mifare_auth(struct rfid_asic_handle *h, u_int8_t cmd, u_int32_t serno, + u_int8_t block) +{ + int ret; + struct mifare_authcmd acmd; + u_int8_t reg; + + if (cmd != RFID_CMD_MIFARE_AUTH1A && cmd != RFID_CMD_MIFARE_AUTH1B) + return -EINVAL; + + /* Initialize acmd */ + acmd.block_address = block & 0xff; + acmd.auth_cmd = cmd; + //acmd.serno = htonl(serno); + acmd.serno = serno; + + ret = rc632_clear_bits(h, RC632_REG_CONTROL, + RC632_CONTROL_CRYPTO1_ON); + + /* Clear Rx CRC */ + ret = rc632_clear_bits(h, RC632_REG_CHANNEL_REDUNDANCY, + RC632_CR_RX_CRC_ENABLE); + if (ret < 0) + return ret; + + /* Send Authent1 Command */ + ret = rc632_fifo_write(h, sizeof(acmd), (unsigned char *)&acmd, 0x03); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_COMMAND, RC632_CMD_AUTHENT1); + if (ret < 0) + return ret; + + /* Wait until transmitter is idle */ + ret = rc632_wait_idle(h, RC632_TMO_AUTH1); + if (ret < 0) + return ret; + + ret = rc632_reg_read(h, RC632_REG_SECONDARY_STATUS, ®); + if (ret < 0) + return ret; + if (reg & 0x07) + return -EIO; + + /* Clear Tx CRC */ + ret = rc632_clear_bits(h, RC632_REG_CHANNEL_REDUNDANCY, + RC632_CR_TX_CRC_ENABLE); + if (ret < 0) + return ret; + + /* Send Authent2 Command */ + ret = rc632_reg_write(h, RC632_REG_COMMAND, RC632_CMD_AUTHENT2); + if (ret < 0) + return ret; + + /* Wait until transmitter is idle */ + ret = rc632_wait_idle(h, RC632_TMO_AUTH1); + if (ret < 0) + return ret; + + /* Check whether authentication was successful */ + ret = rc632_reg_read(h, RC632_REG_CONTROL, ®); + if (ret < 0) + return ret; + + if (!(reg & RC632_CONTROL_CRYPTO1_ON)) + return -EACCES; + + return 0; + +} + +/* transcieve regular frame */ +static int +rc632_mifare_transcieve(struct rfid_asic_handle *handle, + const u_int8_t *tx_buf, unsigned int tx_len, + u_int8_t *rx_buf, unsigned int *rx_len, + u_int64_t timeout, unsigned int flags) +{ + int ret; + u_int8_t rxl = *rx_len & 0xff; + + DEBUGP("entered\n"); + memset(rx_buf, 0, *rx_len); + +#if 1 + ret = rc632_reg_write(handle, RC632_REG_CHANNEL_REDUNDANCY, + (RC632_CR_PARITY_ENABLE | + RC632_CR_PARITY_ODD | + RC632_CR_TX_CRC_ENABLE | + RC632_CR_RX_CRC_ENABLE)); +#else + ret = rc632_clear_bits(handle, RC632_REG_CHANNEL_REDUNDANCY, + RC632_CR_RX_CRC_ENABLE|RC632_CR_TX_CRC_ENABLE); +#endif + if (ret < 0) + return ret; + + ret = rc632_transcieve(handle, tx_buf, tx_len, rx_buf, &rxl, 0x32, 0); + *rx_len = rxl; + if (ret < 0) + return ret; + + + return 0; +} + +struct rfid_asic rc632 = { + .name = "Philips CL RC632", + .fc = ISO14443_FREQ_CARRIER, + .priv.rc632 = { + .fn = { + .power_up = &rc632_power_up, + .power_down = &rc632_power_down, + .turn_on_rf = &rc632_turn_on_rf, + .turn_off_rf = &rc632_turn_off_rf, + .transcieve = &rc632_iso14443ab_transcieve, + .iso14443a = { + .init = &rc632_iso14443a_init, + .transcieve_sf = &rc632_iso14443a_transcieve_sf, + .transcieve_acf = &rc632_iso14443a_transcieve_acf, + .set_speed = &rc632_iso14443a_set_speed, + }, + .iso14443b = { + .init = &rc632_iso14443b_init, + }, + .iso15693 = { + .init = &rc632_iso15693_init, + }, + .mifare_classic = { + .setkey = &rc632_mifare_set_key, + .auth = &rc632_mifare_auth, + }, + }, + }, +}; diff --git a/src/rfid_iso14443_common.c b/src/rfid_iso14443_common.c new file mode 100644 index 0000000..38fed6b --- /dev/null +++ b/src/rfid_iso14443_common.c @@ -0,0 +1,58 @@ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +static unsigned int fsdi_table[] = { 16, 24, 32, 40, 48, 64, 96, 128, 256 }; + +int iso14443_fsdi_to_fsd(unsigned int *fsd, unsigned char fsdi) +{ + /* ISO 14443-4:2000(E) Section 5.1. */ + if (fsdi > sizeof(fsdi_table)/sizeof(unsigned int)) + return -1; + + *fsd = fsdi_table[fsdi]; + return 0; +} + +int iso14443_fsd_to_fsdi(unsigned char *fsdi, unsigned int fsd) +{ + int i; + + for (i = 0; i < sizeof(fsdi_table)/sizeof(unsigned int); i++) { + if (fsdi_table[i] == fsd) { + *fsdi = i; + return 0; + } + } + + return -1; +} + +/* calculate the fsd that is equal or the closest smaller value + * that can be coded as fsd */ +unsigned int iso14443_fsd_approx(unsigned int fsd) +{ + unsigned int tbl_size = sizeof(fsdi_table)/sizeof(unsigned int); + int i; + + for (i = tbl_size-1; i >= 0; i--) { + if (fsdi_table[i] <= fsd) + return fsdi_table[i]; + } + /* not reached: return smallest possible FSD */ + return fsdi_table[0]; +} + diff --git a/src/rfid_iso14443_common.h b/src/rfid_iso14443_common.h new file mode 100644 index 0000000..848d983 --- /dev/null +++ b/src/rfid_iso14443_common.h @@ -0,0 +1,11 @@ +#ifndef __RFID_ISO14443_COMMON_H +#define __RFID_ISO14443_COMMON_H + +int iso14443_fsdi_to_fsd(unsigned int *fsd, unsigned char fsdi); +int iso14443_fsd_to_fsdi(unsigned char *fsdi, unsigned int fsd); +unsigned int iso14443_fsd_approx(unsigned int fsd); + +#define ISO14443_FREQ_CARRIER 13560000 +#define ISO14443_FREQ_SUBCARRIER (ISO14443_FREQ_CARRIER/16) + +#endif diff --git a/src/rfid_layer2.c b/src/rfid_layer2.c new file mode 100644 index 0000000..8d1544a --- /dev/null +++ b/src/rfid_layer2.c @@ -0,0 +1,109 @@ +/* librfid - layer 2 protocol handler + * (C) 2005 by Harald Welte + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include + +static struct rfid_layer2 *rfid_layer2_list; + +struct rfid_layer2_handle * +rfid_layer2_init(struct rfid_reader_handle *rh, unsigned int id) +{ + struct rfid_layer2 *p; + + for (p = rfid_layer2_list; p; p = p->next) + if (p->id == id) + return p->fn.init(rh); + + DEBUGP("unable to find matching layer2 protocol\n"); + return NULL; +} + +int +rfid_layer2_open(struct rfid_layer2_handle *ph) +{ + if (!ph->l2->fn.open) + return 0; + + return ph->l2->fn.open(ph); +} + +int +rfid_layer2_transcieve(struct rfid_layer2_handle *ph, + enum rfid_frametype frametype, + const unsigned char *tx_buf, unsigned int len, + unsigned char *rx_buf, unsigned int *rx_len, + u_int64_t timeout, unsigned int flags) +{ + if (!ph->l2->fn.transcieve) + return -EIO; + + return ph->l2->fn.transcieve(ph, frametype, tx_buf, len, rx_buf, + rx_len, timeout, flags); +} + +int rfid_layer2_fini(struct rfid_layer2_handle *ph) +{ + if (!ph->l2->fn.fini) + return 0; + + return ph->l2->fn.fini(ph); +} + +int +rfid_layer2_close(struct rfid_layer2_handle *ph) +{ + if (!ph->l2->fn.close) + return 0; + + return ph->l2->fn.close(ph); +} + +int +rfid_layer2_register(struct rfid_layer2 *p) +{ + p->next = rfid_layer2_list; + rfid_layer2_list = p; + + return 0; +} + +int +rfid_layer2_getopt(struct rfid_layer2_handle *ph, int optname, + void *optval, unsigned int *optlen) +{ + if (!ph->l2->fn.getopt) + return -EINVAL; + + return ph->l2->fn.getopt(ph, optname, optval, optlen); +} + +int +rfid_layer2_setopt(struct rfid_layer2_handle *ph, int optname, + const void *optval, unsigned int optlen) +{ + if (!ph->l2->fn.setopt) + return -EINVAL; + + return ph->l2->fn.setopt(ph, optname, optval, optlen); +} diff --git a/src/rfid_layer2_iso14443a.c b/src/rfid_layer2_iso14443a.c new file mode 100644 index 0000000..da2628f --- /dev/null +++ b/src/rfid_layer2_iso14443a.c @@ -0,0 +1,323 @@ +/* ISO 14443-3 A anticollision implementation + * + * (C) 2005 by Harald Welte + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TIMEOUT 1236 + +/* Transcieve a 7-bit short frame */ +static int +iso14443a_transcieve_sf(struct rfid_layer2_handle *handle, + unsigned char cmd, + struct iso14443a_atqa *atqa) +{ + struct rfid_reader *rdr = handle->rh->reader; + + return rdr->iso14443a.transcieve_sf(handle->rh, cmd, atqa); +} + +/* Transmit an anticollission bit frame */ +static int +iso14443a_transcieve_acf(struct rfid_layer2_handle *handle, + struct iso14443a_anticol_cmd *acf, + unsigned int *bit_of_col) +{ + struct rfid_reader *rdr = handle->rh->reader; + + return rdr->iso14443a.transcieve_acf(handle->rh, acf, bit_of_col); +} + +/* Transmit a regular frame */ +static int +iso14443a_transcieve(struct rfid_layer2_handle *handle, + enum rfid_frametype frametype, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len, + u_int64_t timeout, unsigned int flags) +{ + return handle->rh->reader->transcieve(handle->rh, frametype, tx_buf, + tx_len, rx_buf, rx_len, timeout, flags); +} + +static int +iso14443a_code_nvb_bits(unsigned char *nvb, unsigned int bits) +{ + unsigned int byte_count = bits / 8; + unsigned int bit_count = bits % 8; + + if (byte_count < 2 || byte_count > 7) + return -1; + + *nvb = ((byte_count & 0xf) << 4) | bit_count; + + return 0; +} + +/* first bit is '1', second bit '2' */ +static void +set_bit_in_field(unsigned char *bitfield, unsigned int bit) +{ + unsigned int byte_count = bit / 8; + unsigned int bit_count = bit % 8; + + DEBUGP("bitfield=%p, byte_count=%u, bit_count=%u\n", + bitfield, byte_count, bit_count); + DEBUGP("%p = 0x%02x\n", (bitfield+byte_count), *(bitfield+byte_count)); + *(bitfield+byte_count) |= 1 << (bit_count-1); + DEBUGP("%p = 0x%02x\n", (bitfield+byte_count), *(bitfield+byte_count)); +} + +static int +iso14443a_anticol(struct rfid_layer2_handle *handle) +{ + int ret; + unsigned int uid_size; + struct iso14443a_handle *h = &handle->priv.iso14443a; + struct iso14443a_atqa atqa; + struct iso14443a_anticol_cmd acf; + unsigned int bit_of_col; + unsigned char sak[3]; + unsigned int rx_len = sizeof(sak); + char *aqptr = (char *) &atqa; + + memset(handle->uid, 0, sizeof(handle->uid)); + memset(sak, 0, sizeof(sak)); + memset(&atqa, 0, sizeof(atqa)); + memset(&acf, 0, sizeof(acf)); + + ret = iso14443a_transcieve_sf(handle, ISO14443A_SF_CMD_REQA, &atqa); + if (ret < 0) { + h->state = ISO14443A_STATE_REQA_SENT; + DEBUGP("error during transcieve_sf: %d\n", ret); + return ret; + } + h->state = ISO14443A_STATE_ATQA_RCVD; + + DEBUGP("ATQA: 0x%02x 0x%02x\n", *aqptr, *(aqptr+1)); + + if (!atqa.bf_anticol) { + h->state = ISO14443A_STATE_NO_BITFRAME_ANTICOL; + DEBUGP("no bitframe anticollission bits set, aborting\n"); + return -1; + } + + if (atqa.uid_size == 2 || atqa.uid_size == 3) + uid_size = 3; + else if (atqa.uid_size == 1) + uid_size = 2; + else + uid_size = 1; + + acf.sel_code = ISO14443A_AC_SEL_CODE_CL1; + + h->state = ISO14443A_STATE_ANTICOL_RUNNING; + h->level = ISO14443A_LEVEL_CL1; + +cascade: + iso14443a_code_nvb_bits(&acf.nvb, 16); + + ret = iso14443a_transcieve_acf(handle, &acf, &bit_of_col); + if (ret < 0) + return ret; + DEBUGP("bit_of_col = %u\n", bit_of_col); + + while (bit_of_col != ISO14443A_BITOFCOL_NONE) { + set_bit_in_field(&acf.uid_bits[0], bit_of_col-16); + iso14443a_code_nvb_bits(&acf.nvb, bit_of_col); + ret = iso14443a_transcieve_acf(handle, &acf, &bit_of_col); + DEBUGP("bit_of_col = %u\n", bit_of_col); + if (ret < 0) + return ret; + } + + iso14443a_code_nvb_bits(&acf.nvb, 7*8); + ret = iso14443a_transcieve(handle, RFID_14443A_FRAME_REGULAR, + (unsigned char *)&acf, 7, + (unsigned char *) &sak, &rx_len, + TIMEOUT, 0); + if (ret < 0) + return ret; + + if (sak[0] & 0x04) { + /* Cascade bit set, UID not complete */ + switch (acf.sel_code) { + case ISO14443A_AC_SEL_CODE_CL1: + /* cascading from CL1 to CL2 */ + if (acf.uid_bits[0] != 0x88) { + DEBUGP("Cascade bit set, but UID0 != 0x88\n"); + return -1; + } + memcpy(&handle->uid[0], &acf.uid_bits[1], 3); + acf.sel_code = ISO14443A_AC_SEL_CODE_CL2; + h->level = ISO14443A_LEVEL_CL2; + break; + case ISO14443A_AC_SEL_CODE_CL2: + /* cascading from CL2 to CL3 */ + memcpy(&handle->uid[3], &acf.uid_bits[1], 3); + acf.sel_code = ISO14443A_AC_SEL_CODE_CL3; + h->level = ISO14443A_LEVEL_CL3; + break; + default: + DEBUGP("cannot cascade any further than CL3\n"); + h->state = ISO14443A_STATE_ERROR; + return -1; + break; + } + goto cascade; + + } else { + switch (acf.sel_code) { + case ISO14443A_AC_SEL_CODE_CL1: + /* single size UID (4 bytes) */ + memcpy(&handle->uid[0], &acf.uid_bits[0], 4); + break; + case ISO14443A_AC_SEL_CODE_CL2: + /* double size UID (7 bytes) */ + memcpy(&handle->uid[3], &acf.uid_bits[0], 4); + break; + case ISO14443A_AC_SEL_CODE_CL3: + /* triple size UID (10 bytes) */ + memcpy(&handle->uid[6], &acf.uid_bits[0], 4); + break; + } + } + + h->level = ISO14443A_LEVEL_NONE; + h->state = ISO14443A_STATE_SELECTED; + + { + if (uid_size == 1) + handle->uid_len = 4; + else if (uid_size == 2) + handle->uid_len = 7; + else + handle->uid_len = 10; + + DEBUGP("UID %s\n", rfid_hexdump(handle->uid, handle->uid_len)); + } + + if (sak[0] & 0x20) { + DEBUGP("we have a T=CL compliant PICC\n"); + h->tcl_capable = 1; + } else { + DEBUGP("we have a T!=CL PICC\n"); + h->tcl_capable = 0; + } + + return 0; +} + +static int +iso14443a_hlta(struct rfid_layer2_handle *handle) +{ + int ret; + unsigned char tx_buf[2] = { 0x50, 0x00 }; + unsigned char rx_buf[10]; + unsigned int rx_len = sizeof(rx_buf); + + ret = iso14443a_transcieve(handle, RFID_14443A_FRAME_REGULAR, + tx_buf, sizeof(tx_buf), + rx_buf, &rx_len, 1000 /* 1ms */, 0); + if (ret < 0) { + /* "error" case: we don't get somethng back from the card */ + return 0; + } + return -1; +} + +static int +iso14443a_setopt(struct rfid_layer2_handle *handle, int optname, + const void *optval, unsigned int optlen) +{ + int ret = -EINVAL; + struct rfid_reader *rdr = handle->rh->reader; + unsigned int speed; + + switch (optname) { + case RFID_OPT_14443A_SPEED_RX: + if (!rdr->iso14443a.set_speed) + return -ENOTSUP; + speed = *(unsigned int *)optval; + ret = rdr->iso14443a.set_speed(handle->rh, 0, speed); + break; + case RFID_OPT_14443A_SPEED_TX: + if (!rdr->iso14443a.set_speed) + return -ENOTSUP; + speed = *(unsigned int *)optval; + ret = rdr->iso14443a.set_speed(handle->rh, 1, speed); + break; + }; + + return ret; +} + + +static struct rfid_layer2_handle * +iso14443a_init(struct rfid_reader_handle *rh) +{ + int ret; + struct rfid_layer2_handle *h = malloc(sizeof(*h)); + if (!h) + return NULL; + + h->l2 = &rfid_layer2_iso14443a; + h->rh = rh; + h->priv.iso14443a.state = ISO14443A_STATE_NONE; + h->priv.iso14443a.level = ISO14443A_LEVEL_NONE; + + ret = h->rh->reader->iso14443a.init(h->rh); + if (ret < 0) { + free(h); + return NULL; + } + + return h; +} + +static int +iso14443a_fini(struct rfid_layer2_handle *handle) +{ + free(handle); + return 0; +} + + +struct rfid_layer2 rfid_layer2_iso14443a = { + .id = RFID_LAYER2_ISO14443A, + .name = "ISO 14443-3 A", + .fn = { + .init = &iso14443a_init, + .open = &iso14443a_anticol, + .transcieve = &iso14443a_transcieve, + .close = &iso14443a_hlta, + .fini = &iso14443a_fini, + .setopt = &iso14443a_setopt, + }, +}; + diff --git a/src/rfid_layer2_iso14443b.c b/src/rfid_layer2_iso14443b.c new file mode 100644 index 0000000..7c92562 --- /dev/null +++ b/src/rfid_layer2_iso14443b.c @@ -0,0 +1,344 @@ +/* ISO 14443-3 B anticollision implementation + * + * (C) 2005 by Harald Welte + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "rfid_iso14443_common.h" + +#define ATQB_TIMEOUT ((256*10e6/ISO14443_FREQ_SUBCARRIER) \ + +(200*10e6/ISO14443_FREQ_SUBCARRIER)) + +static inline int +fwi_to_fwt(struct rfid_layer2_handle *h, unsigned int *fwt, unsigned int fwi) +{ + unsigned int multiplier, tmp; + + /* 15 is RFU */ + if (fwi > 14) + return -1; + + /* According to ISO 14443-3:200(E), Chapter 7.9.4.3, the forumala is + * (256 * 16 / fC) * 2^fwi We avoid floating point computations by + * shifting everything into the microsecond range. In integer + * calculations 1000000*256*16/13560000 evaluates to 302 (instead of + * 302.064897), which provides sufficient precision, IMHO. The max + * result is 302 * 16384 (4947968), which fits well within the 31/32 + * bit range of an integer */ + + multiplier = 1 << fwi; /* 2 to the power of fwi */ + + tmp = (unsigned int) 1000000 * 256 * 16; + + return (tmp / h->rh->ah->asic->fc) * multiplier; +} + +static int +parse_atqb(struct rfid_layer2_handle *h, struct iso14443b_atqb *atqb) +{ + int ret; + + if (atqb->fifty != 0x50) + return -1; + + if (atqb->protocol_info.fo & 0x01) + h->priv.iso14443b.flags |= ISO14443B_CID_SUPPORTED; + if (atqb->protocol_info.fo & 0x02) + h->priv.iso14443b.flags |= ISO14443B_NAD_SUPPORTED; + + ret = fwi_to_fwt(h, &h->priv.iso14443b.fwt, atqb->protocol_info.fwi); + if (ret < 0) { + DEBUGP("invalid fwi %u\n", atqb->protocol_info.fwi); + return ret; + } + + if (atqb->protocol_info.protocol_type == 0x1) { + DEBUGP("we have a T=CL compliant PICC\n"); + h->priv.iso14443b.tcl_capable = 1; + } else { + DEBUGP("we have a T!=CL PICC\n"); + h->priv.iso14443b.tcl_capable = 0; + } + + iso14443_fsdi_to_fsd(&h->priv.iso14443b.fsc, + atqb->protocol_info.max_frame_size); + + /* FIXME: speed capability */ + + memcpy(h->uid, atqb->pupi, sizeof(atqb->pupi)); + h->uid_len = sizeof(atqb->pupi); + + return 0; +} + +static int +send_reqb(struct rfid_layer2_handle *h, unsigned char afi, + unsigned int is_wup, unsigned int num_initial_slots) +{ + int ret; + unsigned char reqb[3]; + struct iso14443b_atqb atqb; + unsigned int atqb_len = sizeof(atqb); + unsigned int num_slot_idx = num_initial_slots; + + reqb[0] = 0x05; + reqb[1] = afi; + + for (num_slot_idx = num_initial_slots; num_slot_idx <= 4; + num_slot_idx++) { + reqb[2] = num_slot_idx & 0x07; + if (is_wup) + reqb[2] |= 0x08; + + ret = h->rh->reader->transcieve(h->rh, RFID_14443B_FRAME_REGULAR, + reqb, sizeof(reqb), + (unsigned char *)&atqb, + &atqb_len, ATQB_TIMEOUT, 0); + h->priv.iso14443b.state = ISO14443B_STATE_REQB_SENT; + if (ret < 0) { + DEBUGP("error during transcieve of REQB/WUBP\n"); + continue; + } + + /* FIXME: send N-1 slot marker frames */ + + if (atqb_len != sizeof(atqb)) { + DEBUGP("error: atqb_len = %u instead of %Zu\n", + atqb_len, sizeof(atqb)); + continue; + } + + /* FIXME: how to detect a collission at 14443B ? I guess we + * can only rely on the CRC checking (CRCErr in ErrorFlag + * register?) */ + + if (parse_atqb(h, &atqb) >= 0) { + h->priv.iso14443b.state = ISO14443B_STATE_ATQB_RCVD; + return 0; + } + } + + return -1; +} + +static inline unsigned int mbli_to_mbl(struct rfid_layer2_handle *h, + unsigned int mbli) +{ + return (h->priv.iso14443b.fsc * 2 ^ (mbli-1)); +} + +static int +transcieve_attrib(struct rfid_layer2_handle *h, const unsigned char *inf, + unsigned int inf_len, unsigned char *rx_data, unsigned int *rx_len) +{ + struct iso14443b_attrib_hdr *attrib; + unsigned int attrib_size = sizeof(*attrib) + inf_len; + unsigned char *rx_buf; + unsigned char fsdi; + int ret = 0; + + DEBUGP("fsd is %u\n", h->priv.iso14443b.fsd); + attrib = malloc(attrib_size); + if (!attrib) { + perror("attrib_alloc"); + return -1; + } + + DEBUGP("fsd is %u\n", h->priv.iso14443b.fsd); + rx_buf = malloc(*rx_len+1); + if (!rx_buf) { + perror("rx_buf malloc"); + ret = -1; + goto out_attrib; + } + + /* initialize attrib frame */ + memset(attrib, 0, attrib_size); + if (inf_len) + memcpy((unsigned char *)attrib+sizeof(*attrib), inf, inf_len); + + attrib->one_d = 0x1d; + memcpy(attrib->identifier, h->uid, 4); + + /* FIXME: do we want to change TR0/TR1 from its default ? */ + /* FIXME: do we want to change SOF/EOF from its default ? */ + + ret = iso14443_fsd_to_fsdi(&fsdi, h->priv.iso14443b.fsd); + if (ret < 0) { + DEBUGP("unable to map FSD(%u) to FSDI\n", + h->priv.iso14443b.fsd); + goto out_rx; + } + attrib->param2.fsdi = fsdi; + + /* FIXME: spd_in / spd_out */ + if (h->priv.iso14443b.tcl_capable == 1) + attrib->param3.protocol_type = 0x1; + + *rx_len = *rx_len + 1; + ret = h->rh->reader->transcieve(h->rh, RFID_14443B_FRAME_REGULAR, + (unsigned char *) attrib, + sizeof(*attrib)+inf_len, + rx_buf, rx_len, h->priv.iso14443b.fwt, + 0); + h->priv.iso14443b.state = ISO14443B_STATE_ATTRIB_SENT; + if (ret < 0) { + DEBUGP("transcieve problem\n"); + goto out_rx; + } + + if ((rx_buf[0] & 0x0f) != h->priv.iso14443b.cid) { + DEBUGP("ATTRIB response with invalid CID %u\n", + rx_buf[0] & 0x0f); + ret = -1; + goto out_rx; + } + + h->priv.iso14443b.state = ISO14443B_STATE_SELECTED; + + h->priv.iso14443b.mbl = mbli_to_mbl(h, (rx_data[0] & 0xf0) >> 4); + + *rx_len = *rx_len - 1; + memcpy(rx_data, rx_buf+1, *rx_len); + +out_rx: + free(rx_buf); +out_attrib: + free(attrib); + + return ret; +} + +static int +iso14443b_hltb(struct rfid_layer2_handle *h) +{ + int ret; + unsigned char hltb[5]; + unsigned char hltb_resp[1]; + unsigned int hltb_len = 1; + + hltb[0] = 0x50; + memcpy(hltb+1, h->uid, 4); + + ret = h->rh->reader->transcieve(h->rh, RFID_14443B_FRAME_REGULAR, + hltb, 5, + hltb_resp, &hltb_len, + h->priv.iso14443b.fwt, 0); + h->priv.iso14443b.state = ISO14443B_STATE_HLTB_SENT; + if (ret < 0) { + DEBUGP("transcieve problem\n"); + return ret; + } + + if (hltb_len != 1 || hltb_resp[0] != 0x00) { + DEBUGP("bad HLTB response\n"); + return -1; + } + h->priv.iso14443b.state = ISO14443B_STATE_HALTED; + + return 0; +} + +static int +iso14443b_anticol(struct rfid_layer2_handle *handle) +{ + unsigned char afi = 0; /* FIXME */ + int ret; + unsigned char buf[255]; + unsigned int buf_len = sizeof(buf); + + ret = send_reqb(handle, afi, 0, 0); + if (ret < 0) + return ret; + + ret = transcieve_attrib(handle, NULL, 0, buf, &buf_len); + if (ret < 0) + return ret; + + return 0; +} + +static struct rfid_layer2_handle * +iso14443b_init(struct rfid_reader_handle *rh) +{ + int ret; + struct rfid_layer2_handle *h = malloc(sizeof(*h)); + if (!h) + return NULL; + + h->l2 = &rfid_layer2_iso14443b; + h->rh = rh; + h->priv.iso14443b.state = ISO14443B_STATE_NONE; + + h->priv.iso14443b.fsd = iso14443_fsd_approx(rh->ah->mru); + DEBUGP("fsd is %u\n", h->priv.iso14443b.fsd); + + /* 14443-3 Section 7.1.6 */ + h->priv.iso14443b.tr0 = (256/ISO14443_FREQ_SUBCARRIER)*10e6; + h->priv.iso14443b.tr1 = (200/ISO14443_FREQ_SUBCARRIER)*10e6; + + ret = h->rh->reader->iso14443b.init(h->rh); + if (ret < 0) { + DEBUGP("error during reader 14443b init\n"); + free(h); + return NULL; + } + + return h; +} + +static int +iso14443b_fini(struct rfid_layer2_handle *handle) +{ + free(handle); + return 0; +} + +static int +iso14443b_transcieve(struct rfid_layer2_handle *handle, + enum rfid_frametype frametype, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len, + u_int64_t timeout, unsigned int flags) +{ + DEBUGP("transcieving %u bytes, expecting max %u\n", tx_len, *rx_len); + return handle->rh->reader->transcieve(handle->rh, frametype, + tx_buf, tx_len, + rx_buf, rx_len, timeout, flags); +} + +struct rfid_layer2 rfid_layer2_iso14443b = { + .id = RFID_LAYER2_ISO14443B, + .name = "ISO 14443-3 B", + .fn = { + .init = &iso14443b_init, + .open = &iso14443b_anticol, + .transcieve = &iso14443b_transcieve, + .close = &iso14443b_hltb, + .fini = &iso14443b_fini, + }, +}; diff --git a/src/rfid_layer2_iso15693.c b/src/rfid_layer2_iso15693.c new file mode 100644 index 0000000..1a93a19 --- /dev/null +++ b/src/rfid_layer2_iso15693.c @@ -0,0 +1,302 @@ +/* ISO 15693 anticollision implementation + * + * (C) 2005 by Harald Welte + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include +#include + +#if 0 +/* Transcieve a 7-bit short frame */ +static int +iso14443a_transcieve_sf(struct rfid_layer2_handle *handle, + unsigned char cmd, + struct iso14443a_atqa *atqa) +{ + struct rfid_reader *rdr = handle->rh->reader; + + return rdr->iso14443a.transcieve_sf(handle->rh, cmd, atqa); +} + +/* Transmit an anticollission bit frame */ +static int +iso14443a_transcieve_acf(struct rfid_layer2_handle *handle, + struct iso14443a_anticol_cmd *acf, + unsigned int *bit_of_col) +{ + struct rfid_reader *rdr = handle->rh->reader; + + return rdr->iso14443a.transcieve_acf(handle->rh, acf, bit_of_col); +} + +/* Transmit a regular frame */ +static int +iso14443a_transcieve(struct rfid_layer2_handle *handle, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len, + u_int64_t, unsigned int flags) +{ + return handle->rh->reader->transcieve(handle->rh, tx_buf, tx_len, + rx_buf, rx_len, timeout, flags); +} + +static int +iso14443a_code_nvb_bits(unsigned char *nvb, unsigned int bits) +{ + unsigned int byte_count = bits / 8; + unsigned int bit_count = bits % 8; + + if (byte_count < 2 || byte_count > 7) + return -1; + + *nvb = ((byte_count & 0xf) << 4) | bit_count; + + return 0; +} + +/* first bit is '1', second bit '2' */ +static void +set_bit_in_field(unsigned char *bitfield, unsigned int bit) +{ + unsigned int byte_count = bit / 8; + unsigned int bit_count = bit % 8; + + DEBUGP("bitfield=%p, byte_count=%u, bit_count=%u\n", + bitfield, byte_count, bit_count); + DEBUGP("%p = 0x%02x\n", (bitfield+byte_count), *(bitfield+byte_count)); + *(bitfield+byte_count) |= 1 << (bit_count-1); + DEBUGP("%p = 0x%02x\n", (bitfield+byte_count), *(bitfield+byte_count)); +} + +static int +iso14443a_anticol(struct rfid_layer2_handle *handle) +{ + int ret; + unsigned int uid_size; + struct iso14443a_atqa atqa; + struct iso14443a_anticol_cmd acf; + unsigned int bit_of_col; + unsigned char sak[3]; + unsigned char uid[10]; // triple size equals 10 bytes; + unsigned int rx_len = sizeof(sak); + char *aqptr = (char *) &atqa; + static int first = 0; + + memset(uid, 0, sizeof(uid)); + memset(sak, 0, sizeof(sak)); + memset(&atqa, 0, sizeof(atqa)); + memset(&acf, 0, sizeof(acf)); + + if (first == 0) { + DEBUGP("Sending REQA\n"); + ret = iso14443a_transcieve_sf(handle, ISO14443A_SF_CMD_REQA, &atqa); + first = 1; + } else { + DEBUGP("Sending WUPA\n"); + ret = iso14443a_transcieve_sf(handle, ISO14443A_SF_CMD_WUPA, &atqa); + } + + if (ret < 0) { + handle->priv.iso14443a.state = ISO14443A_STATE_REQA_SENT; + DEBUGP("error during transcieve_sf: %d\n", ret); + return ret; + } + handle->priv.iso14443a.state = ISO14443A_STATE_ATQA_RCVD; + + DEBUGP("ATQA: 0x%02x 0x%02x\n", *aqptr, *(aqptr+1)); + + if (!atqa.bf_anticol) { + handle->priv.iso14443a.state =ISO14443A_STATE_NO_BITFRAME_ANTICOL; + DEBUGP("no bitframe anticollission bits set, aborting\n"); + return -1; + } + + if (atqa.uid_size == 2 || atqa.uid_size == 3) + uid_size = 3; + else if (atqa.uid_size == 1) + uid_size = 2; + else + uid_size = 1; + + acf.sel_code = ISO14443A_AC_SEL_CODE_CL1; + + handle->priv.iso14443a.state = ISO14443A_STATE_ANTICOL_RUNNING; + handle->priv.iso14443a.level = ISO14443A_LEVEL_CL1; + +cascade: + iso14443a_code_nvb_bits(&acf.nvb, 16); + + ret = iso14443a_transcieve_acf(handle, &acf, &bit_of_col); + if (ret < 0) + return ret; + DEBUGP("bit_of_col = %u\n", bit_of_col); + + while (bit_of_col != ISO14443A_BITOFCOL_NONE) { + set_bit_in_field(&acf.uid_bits[0], bit_of_col-16); + iso14443a_code_nvb_bits(&acf.nvb, bit_of_col); + ret = iso14443a_transcieve_acf(handle, &acf, &bit_of_col); + DEBUGP("bit_of_col = %u\n", bit_of_col); + if (ret < 0) + return ret; + } + + iso14443a_code_nvb_bits(&acf.nvb, 7*8); + ret = iso14443a_transcieve(handle, (unsigned char *)&acf, 7, + (unsigned char *) &sak, &rx_len, + TIMEOUT, 0); + if (ret < 0) + return ret; + + if (sak[0] & 0x04) { + /* Cascade bit set, UID not complete */ + switch (acf.sel_code) { + case ISO14443A_AC_SEL_CODE_CL1: + /* cascading from CL1 to CL2 */ + if (acf.uid_bits[0] != 0x88) { + DEBUGP("Cascade bit set, but UID0 != 0x88\n"); + return -1; + } + memcpy(&uid[0], &acf.uid_bits[1], 3); + acf.sel_code = ISO14443A_AC_SEL_CODE_CL2; + handle->priv.iso14443a.level = ISO14443A_LEVEL_CL2; + break; + case ISO14443A_AC_SEL_CODE_CL2: + /* cascading from CL2 to CL3 */ + memcpy(&uid[3], &acf.uid_bits[1], 3); + acf.sel_code = ISO14443A_AC_SEL_CODE_CL3; + handle->priv.iso14443a.level = ISO14443A_LEVEL_CL3; + break; + default: + DEBUGP("cannot cascade any further than CL3\n"); + handle->priv.iso14443a.state = ISO14443A_STATE_ERROR; + return -1; + break; + } + goto cascade; + + } else { + switch (acf.sel_code) { + case ISO14443A_AC_SEL_CODE_CL1: + /* single size UID (4 bytes) */ + memcpy(&uid[0], &acf.uid_bits[0], 4); + break; + case ISO14443A_AC_SEL_CODE_CL2: + /* double size UID (7 bytes) */ + memcpy(&uid[3], &acf.uid_bits[0], 4); + break; + case ISO14443A_AC_SEL_CODE_CL3: + /* triple size UID (10 bytes) */ + memcpy(&uid[6], &acf.uid_bits[0], 4); + break; + } + } + + handle->priv.iso14443a.level = ISO14443A_LEVEL_NONE; + handle->priv.iso14443a.state = ISO14443A_STATE_SELECTED; + + { + int uid_len; + if (uid_size == 1) + uid_len = 4; + else if (uid_size == 2) + uid_len = 7; + else + uid_len = 10; + + DEBUGP("UID %s\n", rfid_hexdump(uid, uid_len)); + } + + if (sak[0] & 0x20) { + DEBUGP("we have a T=CL compliant PICC\n"); + handle->priv.iso14443a.tcl_capable = 1; + } else { + DEBUGP("we have a T!=CL PICC\n"); + handle->priv.iso14443a.tcl_capable = 0; + } + + return 0; +} + +static int +iso14443a_hlta(struct rfid_layer2_handle *handle) +{ + int ret; + unsigned char tx_buf[2] = { 0x50, 0x00 }; + unsigned char rx_buf[10]; + unsigned int rx_len = sizeof(rx_buf); + + return 0; + + ret = iso14443a_transcieve(handle, tx_buf, sizeof(tx_buf), + rx_buf, &rx_len, 1000 /* 1ms */, 0); + if (ret < 0) { + /* "error" case: we don't get somethng back from the card */ + return 0; + } + return -1; +} +#endif + +static struct rfid_layer2_handle * +iso15693_init(struct rfid_reader_handle *rh) +{ + int ret; + struct rfid_layer2_handle *h = malloc(sizeof(*h)); + if (!h) + return NULL; + + h->l2 = &rfid_layer2_iso15693; + h->rh = rh; + h->priv.iso15693.state = ISO15693_STATE_NONE; + + ret = h->rh->reader->iso15693.init(h->rh); + if (ret < 0) { + free(h); + return NULL; + } + + return h; +} + +static int +iso15693_fini(struct rfid_layer2_handle *handle) +{ + free(handle); + return 0; +} + + +struct rfid_layer2 rfid_layer2_iso15693 = { + .id = RFID_LAYER2_ISO15693, + .name = "ISO 15693", + .fn = { + .init = &iso15693_init, + //.open = &iso15693_anticol, + //.transcieve = &iso15693_transcieve, + //.close = &iso14443a_hlta, + .fini = &iso15693_fini, + }, +}; + diff --git a/src/rfid_proto_mifare_classic.c b/src/rfid_proto_mifare_classic.c new file mode 100644 index 0000000..e2ee80c --- /dev/null +++ b/src/rfid_proto_mifare_classic.c @@ -0,0 +1,151 @@ + +/* Mifare Classic implementation, PCD side. + * + * (C) 2005 by Harald Welte + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "rfid_iso14443_common.h" + + +#define MIFARE_UL_CMD_WRITE 0xA2 +#define MIFARE_UL_CMD_READ 0x30 + +/* FIXME */ +#define MIFARE_CL_READ_FWT 100 +#define MIFARE_CL_WRITE_FWT 100 + +static int +mfcl_read(struct rfid_protocol_handle *ph, unsigned int page, + unsigned char *rx_data, unsigned int *rx_len) +{ + unsigned char rx_buf[16]; + unsigned int real_rx_len = sizeof(rx_buf); + unsigned char tx[2]; + int ret; + + if (page > MIFARE_CL_PAGE_MAX) + return -EINVAL; + + tx[0] = MIFARE_CL_CMD_READ; + tx[1] = page & 0xff; + + ret = rfid_layer2_transcieve(ph->l2h, RFID_MIFARE_FRAME, tx, + sizeof(tx), rx_buf, &real_rx_len, + MIFARE_CL_READ_FWT, 0); + + if (ret < 0) + return ret; + + if (real_rx_len == 1 && *rx_buf == 0x04) + return -EPERM; + + if (real_rx_len < *rx_len) + *rx_len = real_rx_len; + + memcpy(rx_data, rx_buf, *rx_len); + + return ret; +} + +static int +mfcl_write(struct rfid_protocol_handle *ph, unsigned int page, + unsigned char *tx_data, unsigned int tx_len) +{ + unsigned int i; + unsigned char tx[18]; + unsigned char rx[1]; + unsigned int rx_len; + int ret; + + if (tx_len != 16 || page > MIFARE_CL_PAGE_MAX) + return -EINVAL; + + tx[0] = MIFARE_CL_CMD_WRITE16; + tx[1] = page & 0xff; + + memcpy(tx+2, tx_data, 16); + + ret = rfid_layer2_transcieve(ph->l2h, RFID_MIFARE_FRAME, tx, + sizeof(tx), rx, &rx_len, + MIFARE_CL_WRITE_FWT, 0); + + if (ret < 0) + return ret; + + if (rx[0] != MIFARE_UL_RESP_ACK) + return -EIO; + + return ret; +} + +static struct rfid_protocol_handle * +mfcl_init(struct rfid_layer2_handle *l2h) +{ + struct rfid_protocol_handle *ph; + ph = malloc(sizeof(struct rfid_protocol_handle)); + return ph; +} + +static int mfcl_fini(struct rfid_protocol_handle *ph) +{ + free(ph); + return 0; +} + +struct rfid_protocol rfid_protocol_mfcl = { + .id = RFID_PROTOCOL_MIFARE_CLASSIC, + .name = "Mifare Classic", + .fn = { + .init = &mfcl_init, + .read = &mfcl_read, + .write = &mfcl_write, + .fini = &mfcl_fini, + }, +}; + +int mfcl_set_key(struct rfid_protocol_handle *ph, unsigned char *key) +{ + if (!ph->l2h->rh->reader->mifare_classic.setkey) + return -ENODEV; + + return ph->l2h->rh->reader->mifare_classic.setkey(ph->l2h->rh, key); +} + +int mfcl_auth(struct rfid_protocol_handle *ph, u_int8_t cmd, u_int8_t block) +{ + u_int32_t serno = *((u_int32_t *)ph->l2h->uid); + + if (!ph->l2h->rh->reader->mifare_classic.auth) + return -ENODEV; + + return ph->l2h->rh->reader->mifare_classic.auth(ph->l2h->rh, cmd, + serno, block); +} diff --git a/src/rfid_proto_mifare_ul.c b/src/rfid_proto_mifare_ul.c new file mode 100644 index 0000000..747f38c --- /dev/null +++ b/src/rfid_proto_mifare_ul.c @@ -0,0 +1,162 @@ + +/* Mifare Ultralight implementation, PCD side. + * + * (C) 2005 by Harald Welte + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "rfid_iso14443_common.h" + + +/* FIXME */ +#define MIFARE_UL_READ_FWT 100 +#define MIFARE_UL_WRITE_FWT 100 + +static int +mful_read(struct rfid_protocol_handle *ph, unsigned int page, + unsigned char *rx_data, unsigned int *rx_len) +{ + unsigned char rx_buf[16]; + unsigned int real_rx_len = sizeof(rx_buf); + unsigned char tx[2]; + int ret; + + if (page > MIFARE_UL_PAGE_MAX) + return -EINVAL; + + tx[0] = MIFARE_UL_CMD_READ; + tx[1] = page & 0xff; + + ret = rfid_layer2_transcieve(ph->l2h, RFID_14443A_FRAME_REGULAR, + tx, sizeof(tx), rx_buf, + &real_rx_len, MIFARE_UL_READ_FWT, 0); + + if (ret < 0) + return ret; + + if (real_rx_len < *rx_len) + *rx_len = real_rx_len; + + memcpy(rx_data, rx_buf, *rx_len); + + return ret; +} + +static int +mful_write(struct rfid_protocol_handle *ph, unsigned int page, + unsigned char *tx_data, unsigned int tx_len) +{ + unsigned int i; + unsigned char tx[6]; + unsigned char rx[10]; + unsigned int rx_len = sizeof(rx); + int ret; + + if (tx_len != 4 || page > MIFARE_UL_PAGE_MAX) + return -EINVAL; + + tx[0] = MIFARE_UL_CMD_WRITE; + tx[1] = page & 0xff; + + for (i = 0; i < 4; i++) + tx[2+i] = tx_data[i]; + + ret = rfid_layer2_transcieve(ph->l2h, RFID_14443A_FRAME_REGULAR, + tx, sizeof(tx), rx, &rx_len, + MIFARE_UL_WRITE_FWT, 0); + + if (ret < 0) + return ret; + + if (rx[0] != MIFARE_UL_RESP_ACK) + return -EIO; + + return ret; +} + +static int +mful_transcieve(struct rfid_protocol_handle *ph, + const unsigned char *tx_data, unsigned int tx_len, + unsigned char *rx_data, unsigned int *rx_len, + unsigned int timeout, unsigned int flags) +{ + return -EINVAL; +} + +static struct rfid_protocol_handle * +mful_init(struct rfid_layer2_handle *l2h) +{ + struct rfid_protocol_handle *ph; + ph = malloc(sizeof(struct rfid_protocol_handle)); + return ph; +} + +static int mful_fini(struct rfid_protocol_handle *ph) +{ + free(ph); + return 0; +} + +struct rfid_protocol rfid_protocol_mful = { + .id = RFID_PROTOCOL_MIFARE_UL, + .name = "Mifare Ultralight", + .fn = { + .init = &mful_init, + .read = &mful_read, + .write = &mful_write, + .fini = &mful_fini, + }, +}; + +/* Functions below are not (yet? covered in the generic librfid api */ + + +/* lock a certain page */ +int rfid_mful_lock_page(struct rfid_protocol_handle *ph, unsigned int page) +{ + unsigned char buf[4] = { 0x00, 0x00, 0x00, 0x00 }; + + if (ph->proto != &rfid_protocol_mful) + return -EINVAL; + + if (page < 3 || page > 15) + return -EINVAL; + + if (page > 8) + buf[2] = (1 << page); + else + buf[3] = (1 << (page - 8)); + + return mful_write(ph, MIFARE_UL_PAGE_LOCK, buf, sizeof(buf)); +} + +/* convenience wrapper to lock the otp page */ +int rfid_mful_lock_otp(struct rfid_protocol_handle *ph) +{ + return rfid_mful_lock_page(ph, MIFARE_UL_PAGE_OTP); +} diff --git a/src/rfid_proto_tcl.c b/src/rfid_proto_tcl.c new file mode 100644 index 0000000..901e42c --- /dev/null +++ b/src/rfid_proto_tcl.c @@ -0,0 +1,720 @@ +/* ISO 14443-4 (T=CL) implementation, PCD side. + * + * (C) 2005 by Harald Welte + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "rfid_iso14443_common.h" + +static enum rfid_frametype l2_to_frame(unsigned int layer2) +{ + switch (layer2) { + case RFID_LAYER2_ISO14443A: + return RFID_14443A_FRAME_REGULAR; + break; + case RFID_LAYER2_ISO14443B: + return RFID_14443B_FRAME_REGULAR; + break; + } + return 0; +} + +static unsigned int sfgi_to_sfgt(struct rfid_protocol_handle *h, + unsigned char sfgi) +{ + unsigned int multiplier; + unsigned int tmp; + + if (sfgi > 14) + sfgi = 14; + + multiplier = 1 << sfgi; /* 2 to the power of sfgi */ + + /* ISO 14443-4:2000(E) Section 5.2.5: + * (256 * 16 / h->l2h->rh->ah->fc) * (2 ^ sfgi) */ + tmp = (unsigned int) 1000000 * 256 * 16; + + return (tmp / h->l2h->rh->ah->fc) * multiplier; +} + +static unsigned int fwi_to_fwt(struct rfid_protocol_handle *h, + unsigned char fwi) +{ + unsigned int multiplier, tmp; + + if (fwi > 14) + fwi = 14; + + multiplier = 1 << fwi; /* 2 to the power of fwi */ + + /* ISO 14443-4:2000(E) Section 7.2.: + * (256*16 / h->l2h->rh->ah->fc) * (2 ^ fwi) */ + + tmp = (unsigned int) 1000000 * 256 * 16; + + return (tmp / h->l2h->rh->ah->fc) * multiplier; +} + +/* 4.9seconds as microseconds (4.9 billion seconds) exceeds 2^32 */ +#define activation_fwt(x) (((u_int64_t)1000000 * 65536 / x->l2h->rh->ah->fc)) +#define deactivation_fwt(x) activation_fwt(x) + +static int +tcl_parse_ats(struct rfid_protocol_handle *h, + unsigned char *ats, unsigned int size) +{ + unsigned char len = ats[0]; + unsigned char t0; + unsigned char *cur; + + if (len == 0 || size == 0) + return -1; + + if (size < len) + len = size; + + h->priv.tcl.ta = 0; + + if (len == 1) { + /* FIXME: assume some default values */ + h->priv.tcl.fsc = 32; + h->priv.tcl.ta = 0x80; /* 0x80 (same d for both dirs) */ + h->priv.tcl.sfgt = sfgi_to_sfgt(h, 0); + if (h->l2h->l2->id == RFID_LAYER2_ISO14443A) { + /* Section 7.2: fwi default for type A is 4 */ + h->priv.tcl.fwt = fwi_to_fwt(h, 4); + } else { + /* Section 7.2: fwi for type B is always in ATQB */ + /* Value is assigned in tcl_connect() */ + /* This function is never called for Type B, since it has no (R)ATS */ + } + return 0; + } + + /* guarateed to be at least 2 bytes in size */ + + t0 = ats[1]; + cur = &ats[2]; + + iso14443_fsdi_to_fsd(&h->priv.tcl.fsc, t0 & 0x0f); + + if (t0 & (1 << 4)) { + /* TA is transmitted */ + h->priv.tcl.ta = *cur++; + } + + if (t0 & (1 << 5)) { + /* TB is transmitted */ + h->priv.tcl.sfgt = sfgi_to_sfgt(h, *cur & 0x0f); + h->priv.tcl.fwt = fwi_to_fwt(h, (*cur & 0xf0) >> 4); + cur++; + } + + if (t0 & (1 << 6)) { + /* TC is transmitted */ + if (*cur & 0x01) + h->priv.tcl.flags |= TCL_HANDLE_F_NAD_SUPPORTED; + if (*cur & 0x02) + h->priv.tcl.flags |= TCL_HANDLE_F_CID_SUPPORTED; + cur++; + } + + h->priv.tcl.historical_len = (ats+len) - cur; + h->priv.tcl.historical_bytes = cur; + + return 0; +} + + +/* request an ATS from the PICC */ +static int +tcl_request_ats(struct rfid_protocol_handle *h) +{ + int ret; + unsigned char rats[2]; + unsigned char fsdi; + + if (h->priv.tcl.state != TCL_STATE_INITIAL) + return -1; + + ret = iso14443_fsd_to_fsdi(&fsdi, h->priv.tcl.fsd); + if (ret < 0) { + DEBUGP("unable to encode FSD of %u as FSDI\n", h->priv.tcl.fsd); + return ret; + } + + rats[0] = 0xe0; + rats[1] = (h->priv.tcl.cid & 0x0f) | ((fsdi << 4) & 0xf0); + + /* transcieve (with CRC) */ + ret = rfid_layer2_transcieve(h->l2h, RFID_14443A_FRAME_REGULAR, + rats, 2, h->priv.tcl.ats, + &h->priv.tcl.ats_len, activation_fwt(h), + TCL_TRANSP_F_TX_CRC); + if (ret < 0) { + DEBUGP("transcieve of rats failed\n"); + h->priv.tcl.state = TCL_STATE_RATS_SENT; + /* FIXME: retransmit */ + return ret; + } + h->priv.tcl.state = TCL_STATE_ATS_RCVD; + + ret = tcl_parse_ats(h, h->priv.tcl.ats, h->priv.tcl.ats_len); + if (ret < 0) { + DEBUGP("parsing of ats failed\n"); + return ret; + } + + return 0; +} + +#define ATS_TA_DIV_2 1 +#define ATS_TA_DIV_4 2 +#define ATS_TA_DIV_8 4 + +#define PPS_DIV_8 3 +#define PPS_DIV_4 2 +#define PPS_DIV_2 1 +#define PPS_DIV_1 0 +static unsigned char d_to_di(struct rfid_protocol_handle *h, unsigned char D) +{ + static char DI; + unsigned int speed = h->l2h->rh->reader->iso14443a.speed; + + if ((D & ATS_TA_DIV_8) && (speed & RFID_14443A_SPEED_848K)) + DI = PPS_DIV_8; + else if ((D & ATS_TA_DIV_4) && (speed & RFID_14443A_SPEED_424K)) + DI = PPS_DIV_4; + else if ((D & ATS_TA_DIV_2) && (speed & RFID_14443A_SPEED_212K)) + DI = PPS_DIV_2; + else + DI = PPS_DIV_1; + + return DI; +} + +static unsigned int di_to_speed(unsigned char DI) +{ + switch (DI) { + case PPS_DIV_8: + return RFID_14443A_SPEED_848K; + break; + case PPS_DIV_4: + return RFID_14443A_SPEED_424K; + break; + case PPS_DIV_2: + return RFID_14443A_SPEED_212K; + break; + case PPS_DIV_1: + return RFID_14443A_SPEED_106K; + break; + } +} + +/* start a PPS run (autimatically configure highest possible speed */ +static int +tcl_do_pps(struct rfid_protocol_handle *h) +{ + int ret; + unsigned char ppss[3]; + unsigned char pps_response[1]; + unsigned int rx_len = 1; + unsigned char Dr, Ds, DrI, DsI; + unsigned int speed; + + if (h->priv.tcl.state != TCL_STATE_ATS_RCVD) + return -1; + + Dr = h->priv.tcl.ta & 0x07; + Ds = h->priv.tcl.ta & 0x70 >> 4; + DEBUGP("Dr = 0x%x, Ds = 0x%x\n", Dr, Ds); + + if (Dr != Ds && !(h->priv.tcl.ta & 0x80)) { + /* device supports different divisors for rx and tx, but not + * really ?!? */ + DEBUGP("PICC has contradictory TA, aborting PPS\n"); + return -1; + }; + + /* ISO 14443-4:2000(E) Section 5.3. */ + + ppss[0] = 0xd0 | (h->priv.tcl.cid & 0x0f); + ppss[1] = 0x11; + ppss[2] = 0x00; + + /* FIXME: deal with different speed for each direction */ + DrI = d_to_di(h, Dr); + DsI = d_to_di(h, Ds); + DEBUGP("DrI = 0x%x, DsI = 0x%x\n", DrI, DsI); + + ppss[2] = (ppss[2] & 0xf0) | (DrI | DsI << 2); + + ret = rfid_layer2_transcieve(h->l2h, RFID_14443A_FRAME_REGULAR, + ppss, 3, pps_response, &rx_len, + h->priv.tcl.fwt, TCL_TRANSP_F_TX_CRC); + if (ret < 0) + return ret; + + if (pps_response[0] != ppss[0]) { + DEBUGP("PPS Response != PPSS\n"); + return -1; + } + + speed = di_to_speed(DrI); + + ret = rfid_layer2_setopt(h->l2h, RFID_OPT_14443A_SPEED_RX, + &speed, sizeof(speed)); + if (ret < 0) + return ret; + + ret = rfid_layer2_setopt(h->l2h, RFID_OPT_14443A_SPEED_TX, + &speed, sizeof(speed)); + if (ret < 0) + return ret; + + return 0; +} + + +static int +tcl_build_prologue2(struct tcl_handle *th, + unsigned char *prlg, unsigned int *prlg_len, + unsigned char pcb) +{ + *prlg_len = 1; + + *prlg = pcb; + + if (th->toggle) { + /* we've sent a toggle bit last time */ + th->toggle = 0; + } else { + /* we've not sent a toggle last time: send one */ + th->toggle = 1; + *prlg |= 0x01; + } + + if (th->flags & TCL_HANDLE_F_CID_USED) { + /* ISO 14443-4:2000(E) Section 7.1.1.2 */ + *prlg |= TCL_PCB_CID_FOLLOWING; + (*prlg_len)++; + prlg[*prlg_len] = th->cid & 0x0f; + } + + /* nad only for I-block (0xc0 == 00) */ + if ((th->flags & TCL_HANDLE_F_NAD_USED) && + ((pcb & 0xc0) == 0x00)) { + /* ISO 14443-4:2000(E) Section 7.1.1.3 */ + /* FIXME: in case of chaining only for first frame */ + *prlg |= TCL_PCB_NAD_FOLLOWING; + prlg[*prlg_len] = th->nad; + (*prlg_len)++; + } + + return 0; +} + +static int +tcl_build_prologue_i(struct tcl_handle *th, + unsigned char *prlg, unsigned int *prlg_len) +{ + /* ISO 14443-4:2000(E) Section 7.1.1.1 */ + return tcl_build_prologue2(th, prlg, prlg_len, 0x02); +} + +static int +tcl_build_prologue_r(struct tcl_handle *th, + unsigned char *prlg, unsigned int *prlg_len, + unsigned int nak) +{ + unsigned char pcb = 0xa2; + /* ISO 14443-4:2000(E) Section 7.1.1.1 */ + + if (nak) + pcb |= 0x10; + + return tcl_build_prologue2(th, prlg, prlg_len, pcb); +} + +static int +tcl_build_prologue_s(struct tcl_handle *th, + unsigned char *prlg, unsigned int *prlg_len) +{ + /* ISO 14443-4:2000(E) Section 7.1.1.1 */ + + /* the only S-block from PCD->PICC is DESELECT, + * well, actually there is the S(WTX) response. */ + return tcl_build_prologue2(th, prlg, prlg_len, 0xc2); +} + +/* FIXME: WTXM implementation */ + +static int tcl_prlg_len(struct tcl_handle *th) +{ + int prlg_len = 1; + + if (th->flags & TCL_HANDLE_F_CID_USED) + prlg_len++; + + if (th->flags & TCL_HANDLE_F_NAD_USED) + prlg_len++; + + return prlg_len; +} + +#define max_net_tx_framesize(x) (x->fsc - tcl_prlg_len(x)) + +static int +tcl_connect(struct rfid_protocol_handle *h) +{ + int ret; + + if (h->priv.tcl.state != TCL_STATE_DESELECTED && + h->priv.tcl.state != TCL_STATE_INITIAL) + return -1; + + switch (h->l2h->l2->id) { + case RFID_LAYER2_ISO14443A: + /* Start Type A T=CL Activation Sequence */ + ret = tcl_request_ats(h); + if (ret < 0) + return ret; + + /* Only do PPS if any non-default divisors supported */ + if (h->priv.tcl.ta & 0x77) { + ret = tcl_do_pps(h); + if (ret < 0) + return ret; + } + break; + case RFID_LAYER2_ISO14443B: + /* initialized T=CL state from Type B Activation Data */ + h->priv.tcl.cid = h->l2h->priv.iso14443b.cid; + h->priv.tcl.fsc = h->l2h->priv.iso14443b.fsc; + h->priv.tcl.fsd = h->l2h->priv.iso14443b.fsd; + h->priv.tcl.fwt = h->l2h->priv.iso14443b.fwt; + + /* what about ta? sfgt? */ + + if (h->l2h->priv.iso14443b.flags & ISO14443B_CID_SUPPORTED) + h->priv.tcl.flags |= TCL_HANDLE_F_CID_SUPPORTED; + if (h->l2h->priv.iso14443b.flags & ISO14443B_NAD_SUPPORTED) + h->priv.tcl.flags |= TCL_HANDLE_F_NAD_SUPPORTED; + + switch (h->l2h->priv.iso14443b.state) { + case ISO14443B_STATE_SELECTED: + h->priv.tcl.state = TCL_STATE_ATS_RCVD; + break; + case ISO14443B_STATE_ATTRIB_SENT: + h->priv.tcl.state = TCL_STATE_RATS_SENT; + break; + } + + /* PUPI will be presented as ATS/historical bytes */ + memcpy(h->priv.tcl.ats, h->l2h->uid, 4); + h->priv.tcl.ats_len = 4; + h->priv.tcl.historical_bytes = h->priv.tcl.ats; + + break; + default: + DEBUGP("unsupported l2: %u\n", h->l2h->l2->id); + return -1; + break; + } + + h->priv.tcl.state = TCL_STATE_ESTABLISHED; + + return 0; +} + +static int +tcl_deselect(struct rfid_protocol_handle *h) +{ + /* ISO 14443-4:2000(E) Section 8 */ + int ret; + unsigned char frame[3]; /* 3 bytes prologue, no information */ + unsigned char rx[3]; + unsigned int rx_len = sizeof(rx); + unsigned int prlg_len; + struct tcl_handle *th = &h->priv.tcl; + + if (th->state != TCL_STATE_ESTABLISHED) { + /* FIXME: not sure whether deselect is possible here, + * probably better send a HLTA? */ + } + + /* build DESELECT S-block */ + ret = tcl_build_prologue_s(th, frame, &prlg_len); + if (ret < 0) + return ret; + + ret = rfid_layer2_transcieve(h->l2h, RFID_14443A_FRAME_REGULAR, + frame, prlg_len, rx, + &rx_len, deactivation_fwt(h), + TCL_TRANSP_F_TX_CRC); + if (ret < 0) { + /* FIXME: retransmit, HLT(A|B) */ + return ret; + } + + th->state = TCL_STATE_DESELECTED; + + return 0; +} + +#define is_s_block(x) ((x & 0xc0) == 0xc0) +#define is_r_block(x) ((x & 0xc0) == 0x80) +#define is_i_block(x) ((x & 0xc0) == 0x00) + +static int +tcl_transcieve(struct rfid_protocol_handle *h, + const unsigned char *tx_data, unsigned int tx_len, + unsigned char *rx_data, unsigned int *rx_len, + unsigned int timeout, unsigned int flags) +{ + int ret; + unsigned char *tx_buf, *rx_buf; + unsigned char *_rx_data = rx_data; + unsigned int _rx_len; + unsigned int max_rx_len = *rx_len; /* maximum number of payoload that + caller has requested */ + unsigned int prlg_len; + struct tcl_handle *th = &h->priv.tcl; + + unsigned char *_tx; + unsigned int _tx_len, _timeout; + unsigned char wtx_resp[3]; + unsigned char ack[10]; + unsigned int ack_len; + + if (tx_len > max_net_tx_framesize(th)) { + /* slow path: we need to use chaining */ + return -1; + } + + tx_buf = malloc(tcl_prlg_len(th) + tx_len); + if (!tx_buf) { + ret = -ENOMEM; + goto out; + } + rx_buf = malloc(tcl_prlg_len(th) + *rx_len); + if (!rx_buf) { + ret = -ENOMEM; + goto out_txb; + } + + if (tcl_build_prologue_i(th, tx_buf, &prlg_len) < 0) { + ret = -1; + goto out_rxb; + } + memcpy(tx_buf + prlg_len, tx_data, tx_len); + + /* intialize to data-to-be-transferred */ + _tx = tx_buf; + _tx_len = tx_len+prlg_len; + _timeout = th->fwt; + _rx_len = *rx_len; + *rx_len = 0; + +do_tx: + ret = rfid_layer2_transcieve(h->l2h, l2_to_frame(h->l2h->l2->id), + _tx, _tx_len, + rx_buf, &_rx_len, _timeout, 0); + DEBUGP("l2 transcieve finished\n"); + if (ret < 0) + goto out_rxb; + + if ((*rx_buf & 0x01) != h->priv.tcl.toggle) { + DEBUGP("response with wrong toggle bit\n"); + goto out_rxb; + } + + if (is_r_block(*rx_buf)) { + unsigned int txed = _tx - tx_buf; + DEBUGP("R-Block\n"); + /* Handle ACK frame in case of chaining */ + if (*rx_buf & TCL_PCB_CID_FOLLOWING) { + if (*(rx_buf+1) != h->priv.tcl.cid) { + DEBUGP("CID %u is not valid\n", *(rx_buf)+1); + goto out_rxb; + } + } + /* set up parameters for next frame in chain */ + if (txed < tx_len) { + /* move tx pointer by the amount of bytes transferred + * in last frame */ + _tx += _tx_len; + _tx_len = (tx_len - txed); + if (_tx_len > max_net_tx_framesize(th)) { + /* not last frame in chain */ + _tx_len = max_net_tx_framesize(th); + } else { + /* last frame in chain */ + } + goto do_tx; + } else { + DEBUGP("Received ACK in response to last frame in " + "chain?!? Expected I-frame.\n"); + ret = -1; + goto out_rxb; + } + } else if (is_s_block(*rx_buf)) { + unsigned char inf; + unsigned int prlg_len; + + DEBUGP("S-Block\n"); + /* Handle Wait Time Extension */ + if (*rx_buf & TCL_PCB_CID_FOLLOWING) { + if (_rx_len < 3) { + DEBUGP("S-Block with CID but short len\n"); + ret = -1; + goto out_rxb; + } + if (*(rx_buf+1) != h->priv.tcl.cid) { + DEBUGP("CID %u is not valid\n", *(rx_buf)+1); + goto out_rxb; + } + inf = *(rx_buf+2); + } else + inf = *(rx_buf+1); + + if ((*rx_buf & 0x30) != 0x30) { + DEBUGP("S-Block but not WTX?\n"); + ret = -1; + goto out_rxb; + } + inf &= 0x3f; /* only lower 6 bits code WTXM */ + if (inf == 0 || (inf >= 60 && inf <= 63)) { + DEBUGP("WTXM %u is RFU!\n", inf); + ret = -1; + goto out_rxb; + } + + /* Acknowledge WTXM */ + tcl_build_prologue_s(&h->priv.tcl, wtx_resp, &prlg_len); + /* set two bits that make this block a wtx */ + wtx_resp[0] |= 0x30; + wtx_resp[prlg_len] = inf; + _tx = wtx_resp; + _tx_len = prlg_len+1; + _timeout = th->fwt * inf; + + /* start over with next transcieve */ + goto do_tx; /* FIXME: do transcieve locally since we use + totally different buffer */ + + } else if (is_i_block(*rx_buf)) { + unsigned char *inf = rx_buf+1; + unsigned int net_payload_len; + /* we're actually receiving payload data */ + + DEBUGP("I-Block: "); + if (*rx_buf & TCL_PCB_CID_FOLLOWING) { + if (*(rx_buf+1) != h->priv.tcl.cid) { + DEBUGPC("CID %u is not valid\n", *(rx_buf)+1); + goto out_rxb; + } + inf++; + } + if (*rx_buf & TCL_PCB_NAD_FOLLOWING) { + inf++; + } + net_payload_len = _rx_len - (inf - rx_buf); + DEBUGPC("%u bytes\n", net_payload_len); + memcpy(_rx_data, inf, net_payload_len); + /* increment the number of payload bytes that we actually + * received */ + *rx_len += net_payload_len; + _rx_data += net_payload_len; + + if (*rx_buf & 0x10) { + /* we're not the last frame in the chain, continue rx */ + DEBUGP("not the last frame in the chain, continue\n"); + ack_len = sizeof(ack); + tcl_build_prologue_r(&h->priv.tcl, ack, &ack_len, 0); + _tx = ack; + _tx_len = ack_len; + goto do_tx; + } + } + +out_rxb: + free(rx_buf); +out_txb: + free(tx_buf); +out: + return ret; +} + +static struct rfid_protocol_handle * +tcl_init(struct rfid_layer2_handle *l2h) +{ + struct rfid_protocol_handle *th; + unsigned int mru = l2h->rh->ah->mru; + + th = malloc(sizeof(struct rfid_protocol_handle) + mru); + if (!th) + return NULL; + + /* FIXME: mru should be attribute of layer2 (in case it adds/removes + * some overhead */ + memset(th, 0, sizeof(struct rfid_protocol_handle) + mru); + + /* maximum received ats length equals mru of asic/reader */ + th->priv.tcl.state = TCL_STATE_INITIAL; + th->priv.tcl.ats_len = mru; + th->priv.tcl.toggle = 1; + + th->priv.tcl.fsd = iso14443_fsd_approx(mru); + + return th; +} + +static int +tcl_fini(struct rfid_protocol_handle *ph) +{ + free(ph); + return 0; +} + +struct rfid_protocol rfid_protocol_tcl = { + .id = RFID_PROTOCOL_TCL, + .name = "ISO 14443-4 / T=CL", + .fn = { + .init = &tcl_init, + .open = &tcl_connect, + .transcieve = &tcl_transcieve, + .close = &tcl_deselect, + .fini = &tcl_fini, + }, +}; diff --git a/src/rfid_protocol.c b/src/rfid_protocol.c new file mode 100644 index 0000000..c401a2c --- /dev/null +++ b/src/rfid_protocol.c @@ -0,0 +1,113 @@ +/* librfid - layer 3 protocol handler + * (C) 2005 by Harald Welte + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include + +static struct rfid_protocol *rfid_protocol_list; + +struct rfid_protocol_handle * +rfid_protocol_init(struct rfid_layer2_handle *l2h, unsigned int id) +{ + struct rfid_protocol *p; + struct rfid_protocol_handle *ph = NULL; + + for (p = rfid_protocol_list; p; p = p->next) { + if (p->id == id) { + ph = p->fn.init(l2h); + break; + } + } + + if (!ph) + return NULL; + + ph->proto = p; + ph->l2h = l2h; + + return ph; +} + +int +rfid_protocol_open(struct rfid_protocol_handle *ph) +{ + if (ph->proto->fn.open) + return ph->proto->fn.open(ph); + return 0; +} + +int +rfid_protocol_transcieve(struct rfid_protocol_handle *ph, + const unsigned char *tx_buf, unsigned int len, + unsigned char *rx_buf, unsigned int *rx_len, + unsigned int timeout, unsigned int flags) +{ + return ph->proto->fn.transcieve(ph, tx_buf, len, rx_buf, rx_len, + timeout, flags); +} + +int +rfid_protocol_read(struct rfid_protocol_handle *ph, + unsigned int page, + unsigned char *rx_data, + unsigned int *rx_len) +{ + if (ph->proto->fn.read) + return ph->proto->fn.read(ph, page, rx_data, rx_len); + else + return -EINVAL; +} + +int +rfid_protocol_write(struct rfid_protocol_handle *ph, + unsigned int page, + unsigned char *tx_data, + unsigned int tx_len) +{ + if (ph->proto->fn.write) + return ph->proto->fn.write(ph, page, tx_data, tx_len); + else + return -EINVAL; +} + +int rfid_protocol_fini(struct rfid_protocol_handle *ph) +{ + return ph->proto->fn.fini(ph); +} + +int +rfid_protocol_close(struct rfid_protocol_handle *ph) +{ + if (ph->proto->fn.close) + return ph->proto->fn.close(ph); + return 0; +} + +int +rfid_protocol_register(struct rfid_protocol *p) +{ + p->next = rfid_protocol_list; + rfid_protocol_list = p; + + return 0; +} diff --git a/src/rfid_reader.c b/src/rfid_reader.c new file mode 100644 index 0000000..9486cae --- /dev/null +++ b/src/rfid_reader.c @@ -0,0 +1,65 @@ +/* librfid - core reader handling + * (C) 2005 by Harald Welte + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include + +static struct rfid_reader *rfid_reader_list; + +struct rfid_reader_handle * +rfid_reader_open(void *data, unsigned int id) +{ + struct rfid_reader *p; + + for (p = rfid_reader_list; p; p = p->next) + if (p->id == id) + return p->open(data); + + DEBUGP("unable to find matching reader\n"); + return NULL; +} + +int +rfid_reader_transcieve(struct rfid_reader_handle *rh, + enum rfid_frametype frametype, + const unsigned char *tx_buf, unsigned int len, + unsigned char *rx_buf, unsigned int *rx_len, + u_int64_t timeout, unsigned int flags) +{ + return rh->reader->transcieve(rh, frametype, tx_buf, len, rx_buf, + rx_len, timeout, flags); +} + +void +rfid_reader_close(struct rfid_reader_handle *rh) +{ + rh->reader->close(rh); +} + +int +rfid_reader_register(struct rfid_reader *r) +{ + r->next = rfid_reader_list; + rfid_reader_list = r; + + return 0; +} diff --git a/src/rfid_reader_cm5121.c b/src/rfid_reader_cm5121.c new file mode 100644 index 0000000..697ac64 --- /dev/null +++ b/src/rfid_reader_cm5121.c @@ -0,0 +1,367 @@ +/* Omnikey CardMan 5121 specific RC632 transport layer + * + * (C) 2005 by Harald Welte + * + * The 5121 is an Atmel AT89C5122 based USB CCID reader (probably the same + * design like the 3121). It's CL RC632 is connected via address/data bus, + * not via SPI. + * + * The vendor-supplied reader firmware provides some undocumented extensions + * to CCID (via PC_to_RDR_Escape) that allow access to registers and FIFO of + * the RC632. + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* FIXME */ +#include "rc632.h" + +//#define SENDBUF_LEN 40 +#define SENDBUF_LEN 100 +#define RECVBUF_LEN 40 + +static +int Write1ByteToReg(struct rfid_asic_transport_handle *rath, + unsigned char reg, unsigned char value) +{ + unsigned char sndbuf[SENDBUF_LEN]; + unsigned char rcvbuf[RECVBUF_LEN]; + unsigned int retlen = RECVBUF_LEN; + + sndbuf[0] = 0x20; + sndbuf[1] = 0x00; + sndbuf[2] = 0x01; + sndbuf[3] = 0x00; + sndbuf[4] = 0x00; + sndbuf[5] = 0x00; + sndbuf[6] = reg; + sndbuf[7] = value; + + DEBUGP("reg=0x%02x, val=%02x: ", reg, value); + + if (PC_to_RDR_Escape(rath->data, sndbuf, 8, rcvbuf, + &retlen) == 0) { + DEBUGPC("OK\n"); + return 0; + } + + DEBUGPC("ERROR\n"); + return -1; +} + +static int Read1ByteFromReg(struct rfid_asic_transport_handle *rath, + unsigned char reg, + unsigned char *value) +{ + unsigned char sndbuf[SENDBUF_LEN]; + unsigned char recvbuf[RECVBUF_LEN]; + unsigned int retlen = sizeof(recvbuf); + + sndbuf[0] = 0x20; + sndbuf[1] = 0x00; + sndbuf[2] = 0x00; + sndbuf[3] = 0x00; + sndbuf[4] = 0x01; + sndbuf[5] = 0x00; + sndbuf[6] = reg; + + if (PC_to_RDR_Escape(rath->data, sndbuf, 7, recvbuf, + &retlen) == 0) { + *value = recvbuf[1]; + DEBUGP("reg=0x%02x, val=%02x: ", reg, *value); + DEBUGPC("OK\n"); + return 0; + } + + DEBUGPC("ERROR\n"); + return -1; +} + +static int ReadNBytesFromFIFO(struct rfid_asic_transport_handle *rath, + unsigned char num_bytes, + unsigned char *buf) +{ + unsigned char sndbuf[SENDBUF_LEN]; + unsigned char recvbuf[0x7f]; + unsigned int retlen = sizeof(recvbuf); + + sndbuf[0] = 0x20; + sndbuf[1] = 0x00; + sndbuf[2] = 0x00; + sndbuf[3] = 0x00; + sndbuf[4] = num_bytes; + sndbuf[5] = 0x00; + sndbuf[6] = 0x02; + + DEBUGP("num_bytes=%u: ", num_bytes); + if (PC_to_RDR_Escape(rath->data, sndbuf, 7, recvbuf, &retlen) == 0) { + DEBUGPC("%u [%s]\n", retlen, + rfid_hexdump(recvbuf+1, num_bytes)); + memcpy(buf, recvbuf+1, num_bytes); // len == 0x7f + return 0; + } + + DEBUGPC("ERROR\n"); + return -1; +} + +static int WriteNBytesToFIFO(struct rfid_asic_transport_handle *rath, + unsigned char len, + const unsigned char *bytes, + unsigned char flags) +{ + unsigned char sndbuf[SENDBUF_LEN]; + unsigned char recvbuf[0x7f]; + unsigned int retlen = sizeof(recvbuf); + + sndbuf[0] = 0x20; + sndbuf[1] = 0x00; + sndbuf[2] = len; + sndbuf[3] = 0x00; + sndbuf[4] = 0x00; + sndbuf[5] = flags; + sndbuf[6] = 0x02; + + DEBUGP("%u [%s]: ", len, rfid_hexdump(bytes, len)); + + memcpy(sndbuf+7, bytes, len); + + if (PC_to_RDR_Escape(rath->data, sndbuf, len+7, recvbuf, &retlen) == 0) { + DEBUGPC("OK (%u [%s])\n", retlen, rfid_hexdump(recvbuf, retlen)); + return 0; + } + + DEBUGPC("ERROR\n"); + return -1; +} + +#if 0 +static int TestFIFO(struct rc632_handle *handle) +{ + unsigned char sndbuf[60]; // 0x3c + + // FIXME: repne stosd, call + + memset(sndbuf, 0, sizeof(sndbuf)); + + if (WriteNBytesToFIFO(handle, sizeof(sndbuf), sndbuf, 0) < 0) + return -1; + + return ReadNBytesFromFIFO(handle, sizeof(sndbuf), sndbuf); +} +#endif + +static int cm5121_transcieve(struct rfid_reader_handle *rh, + enum rfid_frametype frametype, + const unsigned char *tx_data, unsigned int tx_len, + unsigned char *rx_data, unsigned int *rx_len, + u_int64_t timeout, unsigned int flags) +{ + return rh->ah->asic->priv.rc632.fn.transcieve(rh->ah, frametype, + tx_data, tx_len, rx_data, + rx_len, timeout, flags); +} + +static int cm5121_transcieve_sf(struct rfid_reader_handle *rh, + unsigned char cmd, struct iso14443a_atqa *atqa) +{ + return rh->ah->asic->priv.rc632.fn.iso14443a.transcieve_sf(rh->ah, + cmd, + atqa); +} + +static int +cm5121_transcieve_acf(struct rfid_reader_handle *rh, + struct iso14443a_anticol_cmd *cmd, + unsigned int *bit_of_col) +{ + return rh->ah->asic->priv.rc632.fn.iso14443a.transcieve_acf(rh->ah, + cmd, bit_of_col); +} + +static int +cm5121_14443a_init(struct rfid_reader_handle *rh) +{ + return rh->ah->asic->priv.rc632.fn.iso14443a.init(rh->ah); +} + +static int +cm5121_14443a_set_speed(struct rfid_reader_handle *rh, + unsigned int tx, + unsigned int speed) +{ + u_int8_t rate; + + DEBUGP("setting rate: "); + switch (speed) { + case RFID_14443A_SPEED_106K: + rate = 0x00; + DEBUGPC("106K\n"); + break; + case RFID_14443A_SPEED_212K: + rate = 0x01; + DEBUGPC("212K\n"); + break; + case RFID_14443A_SPEED_424K: + rate = 0x02; + DEBUGPC("424K\n"); + break; + case RFID_14443A_SPEED_848K: + rate = 0x03; + DEBUGPC("848K\n"); + break; + default: + return -EINVAL; + break; + } + return rh->ah->asic->priv.rc632.fn.iso14443a.set_speed(rh->ah, + tx, rate); +} + +static int +cm5121_14443b_init(struct rfid_reader_handle *rh) +{ + return rh->ah->asic->priv.rc632.fn.iso14443b.init(rh->ah); +} + +static int +cm5121_15693_init(struct rfid_reader_handle *rh) +{ + return rh->ah->asic->priv.rc632.fn.iso15693.init(rh->ah); +} + +static int +cm5121_mifare_setkey(struct rfid_reader_handle *rh, const u_int8_t *key) +{ + return rh->ah->asic->priv.rc632.fn.mifare_classic.setkey(rh->ah, key); +} + +static int +cm5121_mifare_auth(struct rfid_reader_handle *rh, u_int8_t cmd, + u_int32_t serno, u_int8_t block) +{ + return rh->ah->asic->priv.rc632.fn.mifare_classic.auth(rh->ah, + cmd, serno, block); +} + +struct rfid_asic_transport cm5121_ccid = { + .name = "CM5121 OpenCT", + .priv.rc632 = { + .fn = { + .reg_write = &Write1ByteToReg, + .reg_read = &Read1ByteFromReg, + .fifo_write = &WriteNBytesToFIFO, + .fifo_read = &ReadNBytesFromFIFO, + }, + }, +}; + +static int cm5121_enable_rc632(struct rfid_asic_transport_handle *rath) +{ + unsigned char tx_buf[1] = { 0x01 }; + unsigned char rx_buf[64]; + unsigned int rx_len = sizeof(rx_buf); + + PC_to_RDR_Escape(rath->data, tx_buf, 1, rx_buf, &rx_len); + printf("received %u bytes from 01 command\n", rx_len); + + return 0; +} + +static struct rfid_reader_handle * +cm5121_open(void *data) +{ + struct rfid_reader_handle *rh; + struct rfid_asic_transport_handle *rath; + + rh = malloc(sizeof(*rh)); + if (!rh) + return NULL; + memset(rh, 0, sizeof(*rh)); + + rath = malloc(sizeof(*rath)); + if (!rath) + goto out_rh; + memset(rath, 0, sizeof(*rath)); + + rath->rat = &cm5121_ccid; + rh->reader = &rfid_reader_cm5121; + + if (cm5121_source_init(rath) < 0) + goto out_rath; + + if (cm5121_enable_rc632(rath) < 0) + goto out_rath; + + rh->ah = rc632_open(rath); + if (!rh->ah) + goto out_rath; + + DEBUGP("returning %p\n", rh); + return rh; + +out_rath: + free(rath); +out_rh: + free(rh); + + return NULL; +} + +static void +cm5121_close(struct rfid_reader_handle *rh) +{ + struct rfid_asic_transport_handle *rath = rh->ah->rath; + rc632_close(rh->ah); + free(rath); + free(rh); +} + +struct rfid_reader rfid_reader_cm5121 = { + .name = "Omnikey CardMan 5121 RFID", + .open = &cm5121_open, + .close = &cm5121_close, + .transcieve = &cm5121_transcieve, + .iso14443a = { + .init = &cm5121_14443a_init, + .transcieve_sf = &cm5121_transcieve_sf, + .transcieve_acf = &cm5121_transcieve_acf, + .speed = RFID_14443A_SPEED_106K | RFID_14443A_SPEED_212K | + RFID_14443A_SPEED_424K, //| RFID_14443A_SPEED_848K, + .set_speed = &cm5121_14443a_set_speed, + }, + .iso14443b = { + .init = &cm5121_14443b_init, + }, + .mifare_classic = { + .setkey = &cm5121_mifare_setkey, + .auth = &cm5121_mifare_auth, + }, +}; + + diff --git a/src/rfid_reader_cm5121_ccid_direct.c b/src/rfid_reader_cm5121_ccid_direct.c new file mode 100644 index 0000000..3161059 --- /dev/null +++ b/src/rfid_reader_cm5121_ccid_direct.c @@ -0,0 +1,36 @@ +/* CM5121 backend for 'internal' CCID driver */ +#include +#include +#include + +#include + +#include "ccid/ccid-driver.h" + +/* this is the sole function required by rfid_reader_cm5121.c */ +int +PC_to_RDR_Escape(void *handle, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len) +{ + int rc; + ccid_driver_t ch = handle; + size_t maxrxlen = *rx_len; + + rc = ccid_transceive_escape (ch, tx_buf, tx_len, + rx_buf, maxrxlen, rx_len); + + return rc; +} + +int cm5121_source_init(struct rfid_asic_transport_handle *rath) +{ + int rc; + + rc = ccid_open_reader(&rath->data, NULL); + if (rc) { + fprintf (stderr, "failed to open CCID reader: %#x\n", rc); + return -1; + } + return 0; +} diff --git a/src/rfid_reader_cm5121_openct.c b/src/rfid_reader_cm5121_openct.c new file mode 100644 index 0000000..4bc879c --- /dev/null +++ b/src/rfid_reader_cm5121_openct.c @@ -0,0 +1,59 @@ +/* CM5121 backend for OpenCT virtual slot */ + +#include + +#include +#include + +/* FIXME: get rid of this global crap. In fact this needs to become part of + * struct rfid_reader_handle */ +static ct_lock_handle lock; +static int slot = 1; + +/* this is the sole function required by rfid_reader_cm5121.c */ +int +PC_to_RDR_Escape(void *handle, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len) +{ + int rc; + ct_handle *h = (ct_handle *) handle; + + rc = ct_card_transact(h, 1, tx_buf, tx_len, rx_buf, *rx_len); + if (rc >= 0) { + *rx_len = rc; + return 0; + } + + return rc; +} + + +int cm5121_source_init(struct rfid_asic_transport_handle *rath) +{ + struct ct_handle *h; + int rc; + unsigned char atr[64]; + + h = ct_reader_connect(0); + if (!h) + return -1; + + printf("acquiring card lock\n"); + rc = ct_card_lock(h, slot, IFD_LOCK_EXCLUSIVE, &lock); + if (rc < 0) { + fprintf(stderr, "error, no card lock\n"); + return -1; + } + + rc = ct_card_reset(h, slot, atr, sizeof(atr)); + if (rc < 0) { + fprintf(stderr, "error, can't reset virtual card\n"); + return -1; + } + + rath->data = h; + + return 0; +} + diff --git a/utils/Makefile.am b/utils/Makefile.am new file mode 100644 index 0000000..cddde17 --- /dev/null +++ b/utils/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include + +bin_PROGRAMS = openct-escape + +openct_escape_SOURCES = openct-escape.c +openct_escape_LDADD = ../src/librfid.la +openct_escape_LDFLAGS = -dynamic + diff --git a/utils/openct-escape.c b/utils/openct-escape.c new file mode 100644 index 0000000..8f087b2 --- /dev/null +++ b/utils/openct-escape.c @@ -0,0 +1,356 @@ +/* -*- linux-c -*- + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +static const char * +hexdump(const void *data, unsigned int len) +{ + static char string[1024]; + unsigned char *d = (unsigned char *) data; + unsigned int i, left; + + string[0] = '\0'; + left = sizeof(string); + for (i = 0; len--; i += 3) { + if (i >= sizeof(string) -4) + break; + snprintf(string+i, 4, " %02x", *d++); + } + return string; +} + +static struct rfid_reader_handle *rh; +static struct rfid_layer2_handle *l2h; +static struct rfid_protocol_handle *ph; + +static int init() +{ + unsigned char buf[0x3f]; + int rc; + + printf("initializing librfid\n"); + rfid_init(); + + printf("opening reader handle\n"); + rh = rfid_reader_open(NULL, RFID_READER_CM5121); + if (!rh) { + fprintf(stderr, "error, no cm5121 handle\n"); + return -1; + } + + printf("opening layer2 handle\n"); + l2h = rfid_layer2_init(rh, RFID_LAYER2_ISO14443A); + //l2h = rfid_layer2_init(rh, RFID_LAYER2_ISO14443B); + if (!l2h) { + fprintf(stderr, "error during iso14443a_init\n"); + return -1; + } + + //rc632_register_dump(rh->ah, buf); + + printf("running layer2 anticol\n"); + rc = rfid_layer2_open(l2h); + if (rc < 0) { + fprintf(stderr, "error during layer2_open\n"); + return rc; + } + + return 0; +} + +static int l3(int protocol) +{ + printf("running layer3 (ats)\n"); + ph = rfid_protocol_init(l2h, protocol); + if (!ph) { + fprintf(stderr, "error during protocol_init\n"); + return -1; + } + if (rfid_protocol_open(ph) < 0) { + fprintf(stderr, "error during protocol_open\n"); + return -1; + } + + printf("we now have layer3 up and running\n"); + + return 0; +} + +static int select_mf(void) +{ + unsigned char cmd[] = { 0x00, 0xa4, 0x00, 0x00, 0x02, 0x3f, 0x00, 0x00 }; + unsigned char ret[256]; + unsigned int rlen = sizeof(ret); + + int rv; + + rv = rfid_protocol_transcieve(ph, cmd, sizeof(cmd), ret, &rlen, 0, 0); + if (rv < 0) + return rv; + + printf("%d: [%s]\n", rlen, hexdump(ret, rlen)); + + return 0; +} + + +static int iso7816_get_challenge(unsigned char len) +{ + unsigned char cmd[] = { 0x00, 0x84, 0x00, 0x00, 0x08 }; + unsigned char ret[256]; + unsigned int rlen = sizeof(ret); + + cmd[4] = len; + + int rv; + + rv = rfid_protocol_transcieve(ph, cmd, sizeof(cmd), ret, &rlen, 0, 0); + if (rv < 0) + return rv; + + printf("%d: [%s]\n", rlen, hexdump(ret, rlen)); + + return 0; +} + +int +iso7816_select_application(void) +{ + unsigned char cmd[] = { 0x00, 0xa4, 0x04, 0x0c, 0x07, + 0xa0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01 }; + unsigned char resp[7]; + unsigned int rlen = sizeof(resp); + + int rv; + + rv = rfid_protocol_transcieve(ph, cmd, sizeof(cmd), resp, &rlen, 0, 0); + if (rv < 0) + return rv; + + /* FIXME: parse response */ + printf("%s\n", hexdump(resp, rlen)); + + return 0; +} + +int +iso7816_select_ef(u_int16_t fid) +{ + unsigned char cmd[7] = { 0x00, 0xa4, 0x02, 0x0c, 0x02, 0x00, 0x00 }; + unsigned char resp[7]; + unsigned int rlen = sizeof(resp); + + int rv; + + cmd[5] = (fid >> 8) & 0xff; + cmd[6] = fid & 0xff; + + rv = rfid_protocol_transcieve(ph, cmd, sizeof(cmd), resp, &rlen, 0, 0); + if (rv < 0) + return rv; + + /* FIXME: parse response */ + printf("%s\n", hexdump(resp, rlen)); + + return 0; +} + +int +iso7816_read_binary(unsigned char *buf, unsigned int *len) +{ + unsigned char cmd[] = { 0x00, 0xb0, 0x00, 0x00, 0x00 }; + unsigned char resp[256]; + unsigned int rlen = sizeof(resp); + + int rv; + + rv = rfid_protocol_transcieve(ph, cmd, sizeof(cmd), resp, &rlen, 0, 0); + if (rv < 0) + return rv; + + /* FIXME: parse response, determine whether we need additional reads */ + + /* FIXME: copy 'len' number of response bytes to 'buf' */ + return 0; +} + +/* wrapper function around SELECT EF and READ BINARY */ +int +iso7816_read_ef(u_int16_t fid, unsigned char *buf, unsigned int *len) +{ + int rv; + + rv = iso7816_select_ef(fid); + if (rv < 0) + return rv; + + return iso7816_read_binary(buf, len); +} + +/* mifare ultralight helpers */ +int +mifare_ulight_write(struct rfid_protocol_handle *ph) +{ + unsigned char buf[4] = { 0xa1, 0xa2, 0xa3, 0xa4 }; + + return rfid_protocol_write(ph, 10, buf, 4); +} + +int +mifare_ulight_blank(struct rfid_protocol_handle *ph) +{ + unsigned char buf[4] = { 0x00, 0x00, 0x00, 0x00 }; + int i, ret; + + for (i = 4; i <= MIFARE_UL_PAGE_MAX; i++) { + ret = rfid_protocol_write(ph, i, buf, 4); + if (ret < 0) + return ret; + } + return 0; +} + +static int +mifare_ulight_read(struct rfid_protocol_handle *ph) +{ + unsigned char buf[20]; + unsigned int len = sizeof(buf); + int ret; + int i; + + for (i = 0; i <= MIFARE_UL_PAGE_MAX; i++) { + ret = rfid_protocol_read(ph, i, buf, &len); + if (ret < 0) + return ret; + + printf("Page 0x%x: %s\n", i, hexdump(buf, 4)); + } + return 0; +} + +/* mifare classic helpers */ +static int +mifare_classic_read(struct rfid_protocol_handle *ph) +{ + unsigned char buf[20]; + unsigned int len = sizeof(buf); + int ret; + int i; + + for (i = 0; i <= MIFARE_CL_PAGE_MAX; i++) { + ret = rfid_protocol_read(ph, i, buf, &len); + if (ret < 0) + return ret; + + printf("Page 0x%x: %s\n", i, hexdump(buf, len)); + } + return 0; +} + + +int main(int argc, char **argv) +{ + int rc; + char buf[0x40]; + int i, protocol; + +#if 0 + if (argc) { + argc--; + argv++; + } + + while (argc) { + if ( !strcmp (*argv, "--list")) { + char *p; + p = ccid_get_reader_list (); + if (!p) + return 1; + fputs (p, stderr); + free (p); + return 0; + } + else if ( !strcmp (*argv, "--debug")) { + ccid_set_debug_level (ccid_set_debug_level (-1) + 1); + argc--; argv++; + } + else + break; + } +#endif + + if (init() < 0) + exit(1); + + //protocol = RFID_PROTOCOL_MIFARE_UL; + //protocol = RFID_PROTOCOL_MIFARE_CLASSIC; + protocol = RFID_PROTOCOL_TCL; + + if (l3(protocol) < 0) + exit(1); + + switch (protocol) { + case RFID_PROTOCOL_TCL: + /* we've established T=CL at this point */ + select_mf(); + + iso7816_select_application(); + iso7816_select_ef(0x011e); + iso7816_select_ef(0x0101); +#if 1 + for (i = 0; i < 4; i++) + iso7816_get_challenge(0x06); +#endif + break; + case RFID_PROTOCOL_MIFARE_UL: + mifare_ulight_read(ph); +#if 0 + mifare_ulight_blank(ph); + mifare_ulight_write(ph); + mifare_ulight_read(ph); +#endif + break; + case RFID_PROTOCOL_MIFARE_CLASSIC: + rc = mfcl_set_key(ph, MIFARE_CL_KEYA_DEFAULT_INFINEON); + if (rc < 0) { + printf("key format error\n"); + exit(1); + } + rc = mfcl_auth(ph, RFID_CMD_MIFARE_AUTH1A, 0); + if (rc < 0) { + printf("mifare auth error\n"); + exit(1); + } else + printf("mifare authe succeeded!\n"); + mifare_classic_read(ph); + break; + } + + rfid_reader_close(rh); + + exit(0); +}