pax_global_header00006660000000000000000000000064122564067310014520gustar00rootroot0000000000000052 comment=94d1ce0d44f6c4bb2726b7cbef4f020f4b9566c6 libfreefare-0.4.0/000077500000000000000000000000001225640673100137675ustar00rootroot00000000000000libfreefare-0.4.0/.gitignore000066400000000000000000000012101225640673100157510ustar00rootroot00000000000000*.o *.la *.lo *.Plo *.pc */.deps/ */.libs/ autom4te.cache/ Makefile Makefile.in INSTALL aclocal.m4 ar-lib config.* configure depcomp install-sh libtool ltmain.sh missing stamp-h1 examples/mifare-classic-format examples/mifare-classic-read-ndef examples/mifare-classic-write-ndef examples/mifare-desfire-access examples/mifare-desfire-create-ndef examples/mifare-desfire-ev1-configure-ats examples/mifare-desfire-ev1-configure-default-key examples/mifare-desfire-ev1-configure-random-uid examples/mifare-desfire-format examples/mifare-desfire-info examples/mifare-desfire-read-ndef examples/mifare-desfire-write-ndef examples/mifare-ultralight-info libfreefare-0.4.0/AUTHORS000066400000000000000000000002441225640673100150370ustar00rootroot00000000000000romuald@libnfc.org:Romuald Conty romain.tartiere:Romain Tartière rtartiere@il4p.fr:Romain Tartière libfreefare-0.4.0/CMakeLists.txt000066400000000000000000000033001225640673100165230ustar00rootroot00000000000000# $Id$ project(libfreefare C) cmake_minimum_required(VERSION 2.6) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") add_definitions("-std=c99") find_package(LIBNFC REQUIRED) find_package(OpenSSL REQUIRED) IF(WIN32) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/config_windows.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/config.h) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/contrib/win32) find_library(WINSOCK_LIB libws2_32.a) set(LIBS ${LIBS} ${WINSOCK_LIB}) ENDIF(WIN32) if(MINGW) # force MinGW-w64 in 32bit mode add_definitions("-m32") set(CMAKE_SHARED_LINKER_FLAGS -m32) set(CMAKE_EXE_LINKER_FLAGS -m32) endif(MINGW) message("CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS}) message("CMAKE_SHARED_LINKER_FLAGS: " ${CMAKE_SHARED_LINKER_FLAGS}) include_directories(${LIBNFC_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/contrib/libutil) set(LIBS ${LIBS} ${LIBNFC_LIBRARIES} ${OPENSSL_LIBRARIES}) option(WITH_DEBUG "Extra debug information is outputted when this is turned on" OFF) if(DEFINED CMAKE_INSTALL_LIBDIR) set(libdir ${CMAKE_INSTALL_LIBDIR}) else(DEFINED CMAKE_INSTALL_LIBDIR) set(CMAKE_INSTALL_LIBDIR lib) set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}) endif(DEFINED CMAKE_INSTALL_LIBDIR) if(DEFINED INCLUDE_INSTALL_DIR) set(includedir ${INCLUDE_INSTALL_DIR}) else(DEFINED INCLUDE_INSTALL_DIR) set(INCLUDE_INSTALL_DIR include) set(includedir ${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR}) endif(DEFINED INCLUDE_INSTALL_DIR) if(NOT DEFINED SHARE_INSTALL_PREFIX) set(SHARE_INSTALL_PREFIX share) endif(NOT DEFINED SHARE_INSTALL_PREFIX) add_subdirectory(libfreefare) add_subdirectory(examples) libfreefare-0.4.0/COPYING000066400000000000000000000226641225640673100150340ustar00rootroot00000000000000The libfreefare source code is licensed by the *modified* GNU Lesser General Public License (LGPL), text provided below. A special exception to the LGPL is included to allow you to distribute a combined work that includes the libfreefare without being obliged to provide the source code for any proprietary components. The exception text is also included at the bottom of this file. The LGPL license text follows. ------------------------------------------------------------------------ GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser 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 Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ------------------------------------------------------------------------ The libfreefare LGPL Exception Text: Any libfreefare source code, whether modified or in it's original release form, or whether in whole or in part, can only be distributed by you under the terms of the GNU Lesser General Public License plus this exception. An independent module is a module which is not derived from or based on the libfreefare. Clause 1: Linking the libfreefare statically or dynamically with other modules is making a combined work based on the libfreefare. Thus, the terms and conditions of the GNU Lesser General Public License cover the whole combination. As a special exception, the copyright holder of the libfreefare gives you permission to link the libfreefare with independent modules that communicate with the libfreefare solely through the libfreefare API interface, regardless of the license terms of these independent modules, and to copy and distribute the resulting combined work under terms of your choice, provided that: - Every copy of the combined work is accompanied by a written statement that details to the recipient the version of the libfreefare used and an offer by yourself to provide the libfreefare source code (including any modifications you may have made) should the recipient request it. - The independent modules add significant and primary functionality to the libfreefare and do not merely extend the existing functionality already present in the libfreefare. libfreefare-0.4.0/ChangeLog000066400000000000000000000623671225640673100155570ustar00rootroot00000000000000Dec 4, 2013 - 0.4.0 ------------------- Fixes: fix spelling mistakes in manpages fix some typo fix bug in mifare_ultralight_read() fix reading page 0x2b of an ULC mifare-classic-read-ndef: skip NULL TLV & proprio TLV mifare-desfire-write-ndef: split PICC key & Tag app key mifare-desfire-write-ndef: fix issues in spilling ndef data to stdout and fix bug fix few memory leaks fix signed/unsigned comparison fix wrong variable usage remove unused variable avoid void* arithmetic GCC extension remove math.h dependency fix various autotools warnings fix many (but not all) compiler warnings Improvements: add support for 7-byte UID MIFARE Classic cards add support Infineon Mifare Classic 1k add test default ULC key in mifare-ultralight-info mifare-classic-format option: read keys from dump mifare-classic-write-ndef: add -y flag to write without confirmation split mifare-desfire-write-ndef into format-ndef & write-ndef add new example: mifare-ultralight-info. add mifare-desfire-read-ndef, read CC in mifare-desfire-write-ndef allow input file for mifare-desfire-write-ndef & small fixes add non-default key option to mifare-desfire-* examples document how to run cutter tests add new test: test_mifare_classic_format_first_sector(). Changes: update code to use libnfc 1.7.0 Special thanks to: - Ludovic Rousseau - Don Coleman - Gernoth - Jan Engelhardt Mar 1, 2012 - 0.3.4 ------------------- Fixes: Improvements: Changes: 2012-03-01 Romuald Conty * NEWS, configure.ac, debian/changelog: prepare libfreefare 0.3.4 2012-03-01 Romain Tartiere * libfreefare/mifare_ultralight.c: Fix Mifare Ultralight C card detection routine (Fixes Issue 86). Mar 1, 2012 - 0.3.3 ------------------- Fixes: Improvements: Changes: 2012-03-01 Romuald Conty * NEWS, configure.ac, debian/changelog, debian/control: prepare libfreefare 0.3.3 2012-01-25 Audrey Diacre * examples/mifare-classic-read-ndef.c, examples/mifare-classic-write-ndef.c, examples/mifare-desfire-access.c, examples/mifare-desfire-ev1-configure-ats.c, examples/mifare-desfire-ev1-configure-default-key.c, examples/mifare-desfire-ev1-configure-random-uid.c, examples/mifare-desfire-format.c, examples/mifare-desfire-info.c, examples/mifare-desfire-write-ndef.c: fix some indentations. 2012-01-25 Audrey Diacre * examples/mifare-classic-format.c, examples/mifare-classic-read-ndef.c, examples/mifare-classic-write-ndef.c, examples/mifare-desfire-access.c, examples/mifare-desfire-ev1-configure-ats.c, examples/mifare-desfire-ev1-configure-default-key.c, examples/mifare-desfire-ev1-configure-random-uid.c, examples/mifare-desfire-format.c, examples/mifare-desfire-info.c, examples/mifare-desfire-write-ndef.c, libfreefare/freefare.c, libfreefare/freefare.h, libfreefare/freefare_internal.h, libfreefare/mifare_classic.c, libfreefare/mifare_desfire.c, libfreefare/mifare_ultralight.c, test/mifare_classic_fixture.c, test/mifare_desfire_ev1_fixture.c, test/mifare_desfire_fixture.c, test/mifare_ultralight_fixture.c: update to use libnfc's trunk 2012-01-15 Romain Tartiere * examples/mifare-classic-format.c, test/mifare_classic_fixture.c, test/mifare_desfire_ev1_fixture.c, test/mifare_desfire_fixture.c, test/mifare_ultralight_fixture.c: Make these functions accept no argument (instead of any). 2012-01-15 Romain Tartiere * libfreefare/freefare.3, libfreefare/freefare_error.3, libfreefare/mad.3, libfreefare/mifare_application.3: Fix inconsistencies in man pages (missing quotes, typos). Oct 10, 2011 - 0.3.2 -------------------- Fixes: Improvements: Changes: 2011-10-17 Romuald Conty * NEWS, README, configure.ac, examples/mifare-classic-read-ndef.c: Prepare libfreefare 0.3.2 2011-10-11 Romuald Conty * examples/mifare-classic-write-ndef.c: mifare-classic-write-ndef could use file as NDEF content (BTW, stdin as input is not yet implemented, any contributions are welcome.) 2011-10-11 Romuald Conty * examples/mifare-classic-read-ndef.c: mifare-classic-read-ndef now close kindly the output stream. 2011-09-29 Romain Tartiere * NEWS, libfreefare/freefare.c, libfreefare/freefare.h: Make freefare_tag_new() public (Fixes issue 64). 2011-09-28 Romuald Conty * examples/mifare-classic-read-ndef.c: svn:keywords fix on examples/mifare-classic-read-ndef.c . 2011-09-22 Romain Tartiere * configure.ac, libfreefare/mifare_classic.c, libfreefare/mifare_desfire.c, libfreefare/mifare_ultralight.c: Sync w/ libnfc-1.5.1. 2011-09-22 Romuald Conty * examples/Makefile.am, examples/mifare-classic-read-ndef.c: Add a new example to extract NDEF Message from a "MIFARE Classic as NFC Forum enable tag". 2011-09-22 Romuald Conty * libfreefare/tlv.3: Minor TLV manpage fix. 2011-09-13 Romuald Conty * debian/control: Improve devian package (Thanks to Thomas Hood) 2011-09-12 Romuald Conty * debian/control: add libssl-dev to Build-Depends (in debian/control). (Fixes Issue 73) 2011-09-12 Romuald Conty * debian/control: add pkg-config to Build-Depends (in debian/control). (Fixes Issue 72) 2011-09-12 Romuald Conty * debian/rules: autoreconf is needed by debian build process when using svn version. 2011-06-27 Romain Tartiere * CMakeLists.txt, cmake/config_windows.h.cmake, libfreefare/CMakeLists.txt, libfreefare/mifare_desfire_crypto.c: Enable WITH_DEBUG for Windows (Fixes issue 65) PR: Issue 65 Submitted by: glenn.ergeerts 2011-05-20 Romuald Conty * debian/control, debian/watch: debian: update pam_nfc, mfoc and libfreefare packages to use dh7. 2011-05-18 Romuald Conty * debian/README.Debian, debian/README.source, debian/changelog, debian/compat, debian/control, debian/copyright, debian/libfreefare-bin.install, debian/libfreefare-dev.install, debian/libfreefare-doc.install, debian/libfreefare0.install, debian/rules, debian/source/format: import debian files 2011-05-11 Romain Tartiere * configure.ac: Fix code coverage with cutter. 2011-04-25 Romain Tartiere * FindLIBNFC.cmake, cmake/config_windows.h.cmake, cmake/modules/FindLIBNFC.cmake, config_windows.h.cmake, contrib/win32/err.h, err.h: Move cmake files around. Update issue 62 Looks like I missed the -p0 argument to patch. Can you please update and test? Thanks! 2011-04-24 Romain Tartiere * libfreefare/mifare_desfire_crypto.c: Indent after last commit. 2011-04-24 Romain Tartiere * libfreefare/mifare_desfire_crypto.c: Merge CRC location / verification in mifare_cryto_postprocess_data(). Fix issue 52. 2011-04-24 Romain Tartiere * CMakeLists.txt, FindLIBNFC.cmake, Makefile.am, NEWS, config_windows.h.cmake, err.h, examples/CMakeLists.txt, libfreefare/CMakeLists.txt: Add Microsoft Windows support. Update issue 62 Status: Feedback Gleen, I slightly modified your patches to better stick with the style of the rest of the project, Can you please confirm that it does not break? PR: Issue 62 Submitted by: glenn.ergeerts 2011-04-23 Romain Tartiere * libfreefare/mifare_desfire.c: Return error if something bad happen in mifare_cryto_preprocess_data() / mifare_cryto_postprocess_data(). Fixes issue 60. PR: Issue 60 Submitted by: ervin.kaljola 2011-04-23 Romain Tartiere * libfreefare/mifare_desfire.c, test/test_mifare_desfire_ev1_aes.c: Fix mifare_desfire_create_value_file(). Fixes issue 59 Committed, thanks! PR: Issue 59 Submitted by: ervin.kaljola 2011-04-23 Romain Tartiere * libfreefare/mifare_desfire_crypto.c: Enhance the mifare_cryto_postprocess_data() function. Update issue 52 Both code snippets are now similar and it should be possible to merge them together. 2011-04-23 Romain Tartiere * test/test_mifare_desfire_ev1_aes.c: Read written data to check that everything is fine. 2011-04-05 Romain Tartiere * libfreefare/mifare_ultralight.c: Implement mifare_ultralight_write() using macros. Update issue 58 I can't reproduce this bug, but saw that the mifare_ultralight_write() was implemented in an old-school way. I updated the code so that you should have better traces when compiling the library in debug mode and running: romain@marvin ~/Projects/libfreefare % cutter -n test_mifare_ultralight_write test ===> 0000 30 07 |0. | <=== 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| ===> 0000 a2 07 12 34 56 78 |...4Vx | ===> 0000 30 07 |0. | <=== 0000 12 34 56 78 00 00 00 00 00 00 00 00 00 00 00 00 |.4Vx............| ===> 0000 a2 07 aa 55 00 ff |...U.. | ===> 0000 30 07 |0. | <=== 0000 aa 55 00 ff 00 00 00 00 00 00 00 00 00 00 00 00 |.U..............| ===> 0000 a2 07 00 00 00 00 |...... | ===> 0000 30 07 |0. | <=== 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| . Finished in 0,548238 seconds (total: 0,069079 seconds) 1 test(s), 12 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s) 100% passed 2011-03-26 Romain Tartiere * libfreefare/mifare_desfire.c: Fix invalid test. 2011-03-26 Romain Tartiere * test/Makefile.am, test/common/Makefile.am: Build unit-tests as part of all-am if DEBUG is enabled, otherwise at check-am before running the test suite. 2011-03-26 Romain Tartiere * NEWS, libfreefare/freefare.h, libfreefare/mifare_desfire.3, libfreefare/mifare_desfire.c, test/test_mifare_desfire_ev1_iso.c: Complete ISO 7816 compatibility for Mifare DESFire EV1 Fixes issue 37 2011-03-25 Romain Tartiere * libfreefare/freefare.h, libfreefare/mifare_desfire.c, test/Makefile.am, test/test_mifare_desfire_ev1_iso.c: Start support of ISO files for Mifare DESFire EV1. Fixes issue 57 New API functions: * mifare_desfire_create_application_iso() * mifare_desfire_create_application_3k3des_iso() * mifare_desfire_create_application_aes_iso() * mifare_desfire_get_df_names() Plus unit test to check this. 2011-03-25 Romain Tartiere * NEWS, libfreefare/freefare.h, libfreefare/mifare_desfire.3, libfreefare/mifare_desfire.c, test/test_mifare_desfire_ev1_3k3des.c, test/test_mifare_desfire_ev1_aes.c: New API functions mifare_desfire_create_application_3k3des(), mifare_desfire_create_application_aes(). Update issue 37 Only ISO application creation as requested by Issue 57 is lacking now. Feb 23, 2011 - 0.3.1 -------------------- Fixes: Improvements: Changes: 2011-02-23 Romain Tartiere * NEWS, configure.ac: Update for 0.3.1. 2011-02-23 Romain Tartiere * libfreefare/mifare_desfire_crypto.c: R is const. 2011-02-13 Romain Tartiere * test/Makefile.am, test/common/Makefile.am: Do not systematically build the tests if cutter is installed. Building them only when running the test-suite is probably enough. 2011-02-13 Romain Tartiere * Makefile.am: Shorter is better. 2011-02-13 Romain Tartiere * NEWS, libfreefare/Makefile.am, libfreefare/freefare.h, libfreefare/freefare_error.3, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_crypto.c, libfreefare/mifare_desfire_error.c, test/test_mifare_desfire.c: Do not abort on crypto error. Because in some circumstances the crypto is skipped regardless of the communication settings by the Mifare DESFire (e.g. when reading a file which is writable with any key), do not abort if the crypto fail, and make it possible to the user to catch such an event to fix his code accordingly. Only display crypto diagnostic if compiled with debug support. 2011-02-13 Romain Tartiere * Makefile.am: prevent myself from shooting in my own foot. 2011-02-13 Romain Tartiere * NEWS, libfreefare/mifare_classic.c: Fix mifare_classic_transfer() with some readers. 2011-01-01 Romain Tartiere * libfreefare/mifare_desfire_crypto.c: Fix CRC localisation code. New issue Summary: Add regression tests for CRC location in encyphered data files. The CRC position location code in mifare_cryto_postprocess_data() shall be checked. new issue Summary: Rework mifare_cryto_postprocess_data() CRC localisations The function has two different implementation of the same feature: locate a CRC at the end of a decyphered stream and check it. The crc32 will fail if the last CRC byte is 0x80, and the crc16 code looks awful. A refactoring would make our life easier. 2011-01-01 Romain Tartiere * examples/mifare-desfire-info.c: Do not exit if the master key settings are not readable. 2010-12-31 Romain Tartiere * libfreefare/mifare_desfire.c: Make mifare_desfire_authenticate() a bit more magic. If the provided key is a 3K3DES key, authenticate in ISO mode. If the key is an AES one, authenticate in AES mode. This sugar should help third party applications to handle authentication in a more generic way. 2010-12-31 Romain Tartiere * libfreefare/mifare_desfire.c: Move away global variables. 2010-12-29 Romain Tartiere * libfreefare/mifare_desfire.c: Fix create_file2(). ECOPYPASTETOOFAST | EBADLUCKITSVALID: The argument communication_settings is not to be used to construct the parameters passed to the crypto functions mifare_cryto_preprocess_data() & mifare_cryto_postprocess_data(), but only for building the frame. 2010-12-29 Romain Tartiere * libfreefare/mifare_desfire.c, test/test_mifare_desfire_ev1_aes.c: Fix write_data() return value when data is enciphered. We only have to return the number of bytes of raw data which are sent. 2010-12-29 Romain Tartiere * libfreefare/mifare_desfire.c: Fix usage of a potentially reallocated memory area. 2010-12-28 Romain Tartiere * libfreefare/freefare.c: Fix parenthesis position. 2010-12-28 Romain Tartiere * libfreefare/Makefile.am, libfreefare/freefare.c, libfreefare/freefare.h, libfreefare/freefare_internal.h, libfreefare/mifare_desfire_crypto.c, libfreefare/mifare_ultralight.c, test/mifare_ultralight_fixture.c, test/test_mifare_ultralight.c: Merge de libfreefare-ultralight branch into trunk (Mifare UltraLightC support). 2010-12-26 Romain Tartiere * NEWS, libfreefare/Makefile.am, libfreefare/mifare_desfire.3: Update man pages. 2010-12-25 Romain Tartiere * examples/mifare-desfire-ev1-configure-ats.c: Set the default ATS instead of a custom one. This prevent some tools to work properly with a card afterthat. 2010-12-24 Romain Tartiere * libfreefare/freefare.c, libfreefare/freefare_internal.h: Detect Mifare DESFire targets with more than one historical character. 2010-12-24 Romain Tartiere * NEWS, examples/Makefile.am, examples/mifare-desfire-ev1-configure-ats.c, libfreefare/freefare.h, libfreefare/mifare_desfire.c: New API function mifare_desfire_set_ats(). 2010-12-24 Romain Tartiere * libfreefare/mifare_desfire_crypto.c: Simplify redundant code. 2010-12-24 Romain Tartiere * examples/mifare-desfire-ev1-configure-default-key.c: Fix example. 2010-12-24 Romain Tartiere * NEWS, examples/Makefile.am, examples/mifare-desfire-ev1-configure-default-key.c, libfreefare/freefare.h, libfreefare/mifare_desfire.c: New API function mifare_desfire_set_default_key(). 2010-12-24 Romain Tartiere * test/test_mifare_desfire_des.c: Commit files forgotten as part of r733. 2010-12-24 Romain Tartiere * libfreefare/freefare_internal.h, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_crypto.c: Rename some internal functions. A function with DES in its name that can perform AES crypto is somewhat disturbing. 2010-12-24 Romain Tartiere * test/test_mifare_desfire.c, test/test_mifare_desfire_ev1.c, test/test_mifare_desfire_ev1_3des.c, test/test_mifare_desfire_ev1_3k3des.c, test/test_mifare_desfire_ev1_aes.c: Freshen out cut_assert_success() macro. 2010-12-24 Romain Tartiere * test/test_mifare_desfire_aes.c: Fix headers for memset(). 2010-12-24 Romain Tartiere * NEWS, libfreefare/Makefile.am, libfreefare/mifare_desfire.3, libfreefare/mifare_desfire_key.3: Sync man pages. 2010-12-24 Audrey Diacre * libfreefare/mifare_desfire_crypto.c, test/test_mifare_desfire_aes.c, test/test_mifare_desfire_ev1_3des.c, test/test_mifare_desfire_ev1_3k3des.c, test/test_mifare_desfire_ev1_aes.c: replace last deprecated bzero function by memset. 2010-12-24 Romain Tartiere * libfreefare/Makefile.am: Create forgotten man pages symlinks. 2010-12-24 Romain Tartiere * libfreefare/freefare_internal.h, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_crypto.c, test/Makefile.am, test/test_mifare_desfire_ev1_3des.c: Add support for ISO authentication with 3DES keys. 2010-12-24 Romain Tartiere * test/test_mifare_desfire_des.c: libfrefare: Use local ivect in test. It looks like finally a global variable modified by a test can impact another test. 2010-12-24 Romain Tartiere * libfreefare/freefare_internal.h, libfreefare/mifare_desfire_crypto.c: Change the enciphered_data_length() internal function prototype. 2010-12-24 Romain Tartiere * libfreefare/freefare_internal.h, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_crypto.c, test/test_mifare_desfire_des.c: Change the mifare_cbc_des() internal function prototype. Dec 23, 2010 - 0.3.0 -------------------- Fixes: Improvements: Changes: 2010-12-23 Romain Tartiere * NEWS: Update the NEWS file regarding the mifare_desfire_get_file_settings() buffer overflow fixed in 0.2.3 (update versions). 2010-12-23 Romain Tartiere * configure.ac: Bump version for the 0.3.x series. 2010-12-18 Romain Tartiere * libfreefare/freefare.c, libfreefare/mad.c, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_crypto.c: Reindent. 2010-12-18 Romain Tartiere * libfreefare/freefare_internal.h, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_crypto.c, test/test_mifare_desfire_des.c: Update the MifareDirection structure for consistency. 2010-12-18 Romain Tartiere * libfreefare/freefare_internal.h, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_crypto.c, test/test_mifare_desfire_des.c: Switch from obscure 'int mac' argument to 'MifareCryptoOperation operation'. 2010-12-18 Romain Tartiere * TODO, libfreefare/Makefile.am, libfreefare/mifare_desfire_authenticate.c, libfreefare/mifare_desfire_crypto.c: Rename mifare_desfire_authenticate.c to mifare_desfire_crypto.c. 2010-12-18 Romain Tartiere * libfreefare/freefare.h, libfreefare/freefare_internal.h, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_authenticate.c, libfreefare/mifare_desfire_key.c, test/Makefile.am, test/common/mifare_desfire_auto_authenticate.c, test/common/mifare_desfire_auto_authenticate.h, test/test_mifare_desfire_ev1_3k3des.c: Add support for authentication using 3K3DES. Please note that according to the NXP documentation of the Mifare DESFire EV1, the mifare_desfire_authenticate_iso() function can be used using either 3DES or 3K3DES keys. The former has not been tested yet and is likely not to work. To word it differently, this is a 3K3DES crypto support, not a ISO authentication support... 2010-12-18 Romain Tartiere * libfreefare/mifare_desfire_authenticate.c: Ignore the status code when deciphering (3)DES data. 2010-12-18 Romain Tartiere * HACKING, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_authenticate.c: Rewrite some tests using switch statements. Add a note for the reason I do this in the HACKING file. 2010-12-18 Romain Tartiere * libfreefare/freefare_internal.h, libfreefare/mifare_desfire_authenticate.c: Improve the way data with already a CRC is preprocessed. 2010-12-18 Romain Tartiere * libfreefare/freefare_internal.h, libfreefare/mifare_desfire_authenticate.c, test/test_mifare_desfire_aes.c: Update CMAC code to handle 64 and 128 bit keys. 2010-12-18 Romain Tartiere * test/test_mifare_desfire_ev1_aes.c: Enlarge another buffer to workaround the read_data() buffer overrun. 2010-12-18 Romain Tartiere * libfreefare/mifare_desfire.c: Invalidate authentication when changing the currently used key. 2010-12-18 Romain Tartiere * libfreefare/freefare_internal.h, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_authenticate.c: Remove weird code. I should quit coding drunk... or maybe start to... 2010-12-18 Romain Tartiere * libfreefare/mifare_desfire.c, test/test_mifare_desfire.c: Workaround invalid write in read_data () (sic) This has to be improved to some extend in the near future (hence the FIXME). 2010-12-17 Romain Tartiere * libfreefare/mifare_desfire_authenticate.c, test/test_mifare_desfire_des.c: Fix key_macing_length() return value when nbytes is 0. 2010-12-17 Romain Tartiere * libfreefare/mifare_desfire.c: Silent down the compiler. The create_application() function has iso_ parameters for creating ISO application but this mode is not available yet so the code is unused which annoys the compiler when told to be somewhat strict. 2010-12-17 Romain Tartiere * test/test_mifare_desfire_ev1_aes.c: Fix the fix introduced at r697. Yes, this type I typed 'make' before committing... 2010-12-17 Romain Tartiere * test/test_mifare_desfire_ev1_aes.c: Fix freed memory usage. 2010-12-15 Romain Tartiere * libfreefare/mifare_desfire.c: Fix a brunch of potentially reallocated memory areas. 2010-12-15 Romain Tartiere * libfreefare/mifare_desfire.c, test/test_mifare_desfire_ev1_aes.c: Fix mifare_desfire_get_key_version() when authenticated with an AES key. 2010-12-15 Romain Tartiere * examples/Makefile.am, libfreefare/freefare.h, libfreefare/mifare_desfire.c: Enable the mifare-desfire-ev1-configure-random-uid example. 2010-12-15 Romain Tartiere * libfreefare/mifare_desfire.c: Fix mifare_desfire_free_mem(). 2010-12-15 Romain Tartiere * examples/mifare-desfire-info.c: Display additional Mifare DESFire EV1 information if applicable. 2010-12-15 Romain Tartiere * NEWS, TODO, configure.ac, examples/mifare-desfire-ev1-configure-random-uid.c, libfreefare/freefare.c, libfreefare/freefare.h, libfreefare/freefare_internal.h, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_authenticate.c, libfreefare/mifare_desfire_key.c, test/Makefile.am, test/common/mifare_desfire_auto_authenticate.c, test/common/mifare_desfire_auto_authenticate.h, test/mifare_desfire_ev1_fixture.c, test/mifare_desfire_ev1_fixture.h, test/test_mifare_desfire.c, test/test_mifare_desfire_aes.c, test/test_mifare_desfire_ev1.c, test/test_mifare_desfire_ev1_aes.c: Add support for Mifare DESFire EV1 with AES encryption. 2010-12-13 Audrey Diacre * libfreefare/mifare_desfire.c: fix buffer overflow. 2010-12-10 Audrey Diacre * configure.ac, libfreefare/mad.c, libfreefare/mifare_classic.c, libfreefare/mifare_desfire.c, libfreefare/mifare_desfire_authenticate.c, test/test_mifare_classic.c, test/test_mifare_desfire.c: replace deprecated bzero function by memset. 2010-12-10 Romain Tartiere * NEWS, libfreefare/mifare_desfire.c: Discard authentication information after deleting the currently selected application. ============================================================================= For older, see https://code.google.com/p/libfreefare/issues/list libfreefare-0.4.0/HACKING000066400000000000000000000106771225640673100147710ustar00rootroot00000000000000Hello hackers! General remarks about contributing ---------------------------------- Contributions to the libfreefare are welcome! Here are some directions to get you started: 1. Install Cutter Cutter is a cool unit test framework for C code. You will need it to run the regression tests suite (`make check') and ensure that your code does not break other features. http://cutter.sourceforge.net/ To cover all tests you'll need several blank cards: * MIFARE Ultralight * MIFARE Ultralight C with default key (BREAKMEIFYOUCAN!) * MIFARE Classic 4k with default keys (FFFFFFFFFF) * DESFire EV1 4k with default PICC key (0000000000000000) After "make check", you can run sub-sets of tests directly with cutter: $ cutter -n /ultralight/ test $ cutter -n /classic/ test $ cutter -n /desfire/ test 2. Follow style conventions The source code of the library trend to follow some conventions so that it is consistent in style and thus easier to read. Basically, it follows FreeBSD's style(9); adding 4-space indentation and 8-space tabs (which you should configure in your editor, e.g. `:set sw=4 ts=8' in vim). You are also advised to `:set cinoptions=t0(0:0` so that you don't have to care about indentation anymore. For more details, please read the style(9) man page from FreeBSD's website: http://www.freebsd.org/cgi/man.cgi?query=style 3. Write tests I told you cutter is lovely in #1 so you really should use it! If you want to contribute code, write test: only code with appropriate tests will be considered. And remember that TDD (Test Driven Development) is cool and writing all tests at the end deeply depressing, so test early, test often! Adding support for a new type of card ------------------------------------- Adding a new supported card to the libfreefare requires a few modification in multiple places. Here is a list of the things to do in order to have the infrastructure ready for hacking the new card support: - Edit libfreefare/freefare.h: - Add your tag to the `mifare_tag_type' enum; - Add a _connect() and a _disconnect() function prototype; - Edit libfreefare/freefare.3: - Add your tag to the `mifare_tag_type' enum documentation; - Edit libfreefare/freefare_internal.h: - Add a new _tag struct. It's very first member SHALL be `struct mifare_tag __tag'; - Add a _tag_new() and a _tag_free() function prototype; - Add a ASSERT_() macro to check the tag's type; - Add a () macro to cast a generic tag to your type. - Edit libfreefare/freefare.c: - Add your tag type to the supported_tags array; - Edit the freefare_get_tags() function so that it calls _tag_new() when it finds your tag; - Edit the freefare_free_tags() function so that it calls _tag_free() to free your tags; - Create libfreefare/.c and implement all that's missing: - _tag_new() MUST allocate all data-structure the tag may need to use during it's lifetime. We do not want to have any function to fail later because the running system is out of resources. Buffers for cryptographic operations on random amount of data MAY however be (re)allocated on demand, in such case refrain from shrinking unnecessarily the buffer size. - _connect() SHOULD initialise data allocated by _tag_new(). Keep in mind that a single tag may be disconnected from and connected to again, without being freed in the meantime. Since all memory allocations are done in _tag_new(), your code SHOULD only care about initialising these data structures; - _disconnect() MAY do more that just send a disconnect command to the tag. At time of writing I have no idea what it could be but who knows... - _tag_free() SHALL free all resources allocated for the tag (surprising, isn't it?) Various guidelines ------------------ - If a given card has different cryptographic modes, you SHOULD use switch/cases to handle specific branches of code, even when applicable to only one cypher. The idea is that if you don't provide support for all cryptographic schemes, or if an evolution of the card provides more cryptographic possibilities, when adding support for a new cypher, the compiler can warn the developer about unhandled values in switch statements. Please refer to the Mifare DESFire code for an example. libfreefare-0.4.0/Makefile.am000066400000000000000000000026461225640673100160330ustar00rootroot00000000000000# $Id$ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = contrib libfreefare test examples pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libfreefare.pc dist-hook: @if ! grep -qi "$$(LC_ALL=C date +'%d %b %Y')" NEWS; then \ printf "\033[31;1mBEWARE! The first line from the NEWS file does not contain the current date!\033[0m\n"; \ sleep 3; \ fi @if test -d "$(srcdir)/.svn"; then \ echo "Creating ChangeLog..." && \ ( cd "$(top_srcdir)" && \ echo '# Generated by Makefile. Do not edit.'; echo; \ $(top_srcdir)/missing --run svn2cl --authors=AUTHORS --strip-prefix=/trunk/libfreefare --stdout ) > ChangeLog.tmp \ && mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \ || ( rm -f ChangeLog.tmp ; \ echo "Failed to generate ChangeLog" >&2 ); \ else \ echo "A svn checkout is required to generate a ChangeLog" >&2; \ fi EXTRA_DIST = HACKING CLEANFILES = coverage.info clean-local: clean-local-coverage .PHONY: clean-local-coverage clean-local-coverage: -rm -rf coverage SVN_KEYWORDS_FILES_LIST_CMD = find $(top_srcdir) -name '*.[hc]' -a ! -name config.h \ -o -name Makefile.am -o -name '*.cmake' -o -name 'CMakeLists.txt' svn-keywords: @echo Update files svn:keywords... @$(SVN_KEYWORDS_FILES_LIST_CMD) | xargs svn propset -q svn:keywords Id $(MAKE) svn-keywords-check svn-keywords-check: @echo "Files missing a '$$"Id"$$' vcs keyword:" @$(SVN_KEYWORDS_FILES_LIST_CMD) | xargs grep -L '\$$Id[^$$]*\$$' libfreefare-0.4.0/NEWS000066400000000000000000000067311225640673100144750ustar00rootroot00000000000000Changes between 0.3.4 and 0.3.5 [20 jan 2013] *) Depends on libnfc 1.7.0 Changes between 0.3.3 and 0.3.4 [1 mar 2012] *) Fix Mifare Ultralight C card detection routine Changes between 0.3.2 and 0.3.3 [1 mar 2012] *) Depends on libnfc 1.6.0 Changes between 0.3.1 and 0.3.2 [17 oct 2011] *) New API functions to ease up creation of applications with strong cryptography on Mifare DESFire EV1: mifare_desfire_create_application_3k3des(), mifare_desfire_create_application_aes(). *) New API functions for ISO 7616 support om Mifare DESFire EV1: mifare_desfire_create_application_iso(), mifare_desfire_create_application_3k3des_iso(), mifare_desfire_create_application_aes_iso(), mifare_desfire_get_df_names(), mifare_desfire_get_iso_file_ids(), mifare_desfire_create_std_data_file_iso(), mifare_desfire_create_backup_data_file_iso(), mifare_desfire_create_linear_record_file_iso(), mifare_desfire_create_cyclic_record_file_iso(). *) New public API function freefare_tag_new(); *) Microsoft Windows support. Changes between 0.3.0 and 0.3.1 [23 feb 2011] *) Fix mifare_classic_transfer() for devices returning a 1 byte response on success. *) New API function mifare_desfire_last_pcd_error(). Changes between 0.2.3 and 0.3.0 [23 dec 2010] *) Add support for ISO and AES authentication through mifare_desfire_authenticate_iso() and mifare_desfire_authenticate_aes(). *) Add support for 3K3DES and AES cryptographic operations and add new functions mifare_desfire_3k3des_key_new(), mifare_desfire_3k3des_key_new_with_version(), mifare_desfire_aes_key_new() and mifare_desfire_aes_key_new_with_version() for generating keys. *) New functions mifare_desfire_free_mem(), mifare_desfire_set_configuration(), mifare_desfire_set_default_key(), mifare_desfire_set_ats(), mifare_desfire_get_card_uid(), for Mifare DESFire EV1 targets manipulation. *) Deprecate authentication information when deleting the currently selected application Changes between 0.2.2 and 0.2.3 [23 dec 2010] *) Fix a buffer overflow in mifare_desfire_get_file_settings(). Changes between 0.2.1 and 0.2.2 [23 nov 2010] *) Fix build when cutter is not available or is disabled. Changes between 0.2.0 and 0.2.1 [23 nov 2010] *) The mifare_desfire_error_lookup() and mifare_desfire_get_last_error() functions were removed and replaced by the freefare_strerror(), freefare_strerror_r() and freefare_perror() functions. *) The library reports errors other that the ones returned by the PICC. *) The MDAD_KEYx macro where renamed MDAR_KEYx for consistency. *) The MDCM_MACING macro was renamed MDCM_MACED. *) The MDCM_FULLDES macro was renamed MDCM_ENCIPHERED. *) New function mifare_desfire_last_picc_error(). *) New function mifare_desfire_aid_get_aid(). Changes between 0.1.0 and 0.2.0 [07 sep 2010] *) New function freefare_free_tag() to free individual tags from a MifareTag list. *) The mifare_application_alloc() size parameter is now expressed in bytes and not in sectors. *) New API function mad_sector_reserved(). *) The mifare_classic_format_sector() functions now require a sector number instead of a block number. *) New API functions mifare_classic_block_sector(), mifare_classic_sector_first_block(), mifare_classic_sector_block_count() and mifare_classic_sector_last_block(). *) New API functions mifare_application_read(), mifare_application_write(). libfreefare-0.4.0/README000066400000000000000000000006421225640673100146510ustar00rootroot00000000000000The libfreefare project aims to provide a convenient API for Mifare card manipulations. Supported tags: - Mifare Classic 1k - Mifare Classic 4k - Mifare DESFire 2K/4K/8K - Mifare DESFire EV1 - Mifare Ultralight - Mifare UltralightC Supported features: - Mifare Application Directory (MAD) v1 - Mifare Application Directory (MAD) v2 - Mifare Application Directory (MAD) v3 (part of DESFire support) libfreefare-0.4.0/TODO000066400000000000000000000013601225640673100144570ustar00rootroot00000000000000[ ] Enforce valid parameters. Some functions will cause a crash when called with invalid parameters (e.g. mifare_classic_authenticate() with a NULL key). [ ] MAD and MifareApplication functions are tied to mifare_clasic_* ones and some refactoring is required to have a consistent API. [ ] The DESFIRE_TRANSCEIVE macro should be replaced by a function DESFIRE_TRANSCEIVE was originaly a macro to enclose a nfc_initiator_trans- ceive_bytes() call with debug lines. The functions has unexpectedly grown up and is now between 25 and 100 lines of code (depending of my refactoring mood). The main drawbacks are poor readability, redundant code in the binary, debuggers don't cope well with macros, and by design it sucks. libfreefare-0.4.0/cmake/000077500000000000000000000000001225640673100150475ustar00rootroot00000000000000libfreefare-0.4.0/cmake/config_windows.h.cmake000066400000000000000000000017761225640673100213310ustar00rootroot00000000000000/*- * Copyright (C) 2011 Glenn Ergeerts. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #ifndef __CONFIG_WINDOWS_H__ #define __CONFIG_WINDOWS_H__ #cmakedefine WITH_DEBUG #include #define htole32(x) (x) #define le32toh(x) (x) #define le16toh(x) (x) #define htobe16(x) htons(x) #define be16toh(x) ntohs(x) #define ENOTSUP WSAEOPNOTSUPP #endif /* !__CONFIG_WINDOWS_H__ */ libfreefare-0.4.0/cmake/modules/000077500000000000000000000000001225640673100165175ustar00rootroot00000000000000libfreefare-0.4.0/cmake/modules/FindLIBNFC.cmake000066400000000000000000000013171225640673100212610ustar00rootroot00000000000000# $Id$ # TODO locate using pkg-config for linux/bsd #set(LIBNFC_INCLUDE_DIRS "") #set(LIBNFC_LIBRARIES "") set(LIBNFC_INSTALL_DIR $ENV{PROGRAMFILES}/libnfc CACHE PATH "libnfc installation directory") message("libnfc install dir: " ${LIBNFC_INSTALL_DIR}) find_path(LIBNFC_INCLUDE_DIRS NAMES nfc/nfc.h PATHS ${LIBNFC_INSTALL_DIR}/include) message("libnfc include dir found: " ${LIBNFC_INCLUDE_DIRS}) find_library(LIBNFC_LIBRARIES libnfc PATHS ${LIBNFC_INSTALL_DIR}/lib) message("libnfc library found: " ${LIBNFC_LIBRARIES}) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBNFC DEFAULT_MSG LIBNFC_INCLUDE_DIRS LIBNFC_LIBRARIES ) MARK_AS_ADVANCED(LIBNFC_INCLUDE_DIRS LIBNFC_LIBRARIES) libfreefare-0.4.0/configure.ac000066400000000000000000000053731225640673100162650ustar00rootroot00000000000000AC_INIT([libfreefare],[0.4.0]) AC_CONFIG_MACRO_DIR([m4]) AC_PROG_CC AC_PROG_CXX m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) LT_INIT AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE(dist-bzip2 no-dist-gzip) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) # Debug support (default:no) AC_ARG_ENABLE([debug],AS_HELP_STRING([--enable-debug],[Enable debug output]),[enable_debug=$enableval],[enable_debug="no"]) if test x"$enable_debug" = xyes; then AC_CHECK_LIB([util], [hexdump], [has_libutil=yes], [has_libutil=no]) fi AM_CONDITIONAL(WITH_DEBUG, [test x"$enable_debug" = xyes]) AM_CONDITIONAL(HAS_LIBUTIL, [test x"$has_libutil" = xyes]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_HEADER_STDBOOL AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_OFF_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T # Checks for library functions. AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_HEADERS([sys/types.h]) AC_CHECK_FUNCS([memset letoh32 htole32 pow strdup strerror]) AC_CHECK_HEADERS([endian.h sys/endian.h CoreFoundation/CoreFoundation.h]) if test $ac_cv_header_endian_h = "no" -a $ac_cv_header_sys_endian_h = "no" -a $ac_cv_header_CoreFoundation_CoreFoundation_h = "no"; then AC_MSG_ERROR(["Can't locate usable header file for endianness convertions."]); fi AC_CHECK_HEADERS([byteswap.h]) AC_DEFINE([_XOPEN_SOURCE], [600], [Define to 500 if Single Unix conformance is wanted, 600 for sixth revision.]) AC_DEFINE([_BSD_SOURCE], [1], [Define on BSD to activate all library features]) CFLAGS="$CFLAGS -std=c99" # Crypto functions for MIFARE DesFire support are provided by OpenSSL. AC_CHECK_LIB([crypto], [DES_ecb_encrypt], [], [AC_MSG_ERROR([Cannot find libcrypto.])]) AC_CHECK_HEADERS([openssl/aes.h openssl/des.h openssl/rand.h], [], [AC_MSG_ERROR([Cannot find openssl headers.])]) # Checks for pkg-config modules. LIBNFC_REQUIRED_VERSION="1.7.0" PKG_CHECK_MODULES([LIBNFC], [libnfc >= $LIBNFC_REQUIRED_VERSION], [], [AC_MSG_ERROR([libnfc >= $LIBNFC_REQUIRED_VERSION is mandatory.])]) PKG_CONFIG_REQUIRES="libnfc" AC_SUBST([PKG_CONFIG_REQUIRES]) CUTTER_REQUIRED_VERSION=1.1.7 m4_ifdef([AC_CHECK_CUTTER], [AC_CHECK_CUTTER([>= $CUTTER_REQUIRED_VERSION])], [ac_cv_use_cutter="no"]) if test x$ac_cv_with_cutter = xyes -a x$ac_cv_use_cutter = xno; then AC_MSG_ERROR([cutter >= $CUTTER_REQUIRED_VERSION is mandatory.]) fi AM_CONDITIONAL([WITH_CUTTER], [test "$ac_cv_use_cutter" != "no"]) m4_ifdef([AC_CHECK_COVERAGE], [AC_CHECK_COVERAGE]) if test x$cutter_enable_coverage = xyes; then CFLAGS="$CFLAGS -O0 -fprofile-arcs -ftest-coverage" fi AC_CONFIG_FILES([Makefile contrib/Makefile contrib/libutil/Makefile examples/Makefile libfreefare/Makefile libfreefare.pc test/Makefile test/common/Makefile]) AC_OUTPUT libfreefare-0.4.0/contrib/000077500000000000000000000000001225640673100154275ustar00rootroot00000000000000libfreefare-0.4.0/contrib/Makefile.am000066400000000000000000000001021225640673100174540ustar00rootroot00000000000000# $Id$ SUBDIRS = libutil EXTRA_DIST = libfreefare_zsnippets.vim libfreefare-0.4.0/contrib/libfreefare_zsnippets.vim000066400000000000000000000044351225640673100225370ustar00rootroot00000000000000"- " Copyright (c) 2010 Romain Tartiere. All rights reserved. " " Redistribution and use, with or without modification, are permitted provided " that the following conditions are met: " 1. Redistributions of source code must retain the above copyright " notice, this list of conditions and the following disclaimer. " " THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$ " Additionnal snippets for working with libfreefare in vim with the " snippetsEmu script: " http://www.vim.org/scripts/script.php?script_id=1318 " " (Contribution for porting this to better snippet handler are welcome!) if !exists('loaded_snippet') || &cp finish endif let st = g:snip_start_tag let et = g:snip_end_tag let cd = g:snip_elem_delim exec "Snippet m mifare_".st.et exec "Snippet mifare_c mifare_classic_".st.et exec "Snippet mifare_d mifare_desfire_".st.et exec "Snippet mifare_u mifare_ultralight_".st.et exec "Snippet mc mifare_classic_".st.et exec "Snippet md mifare_desfire_".st.et exec "Snippet mu mifare_ultralight_".st.et exec "Snippet MC MIFARE_CLASSIC".st.et exec "Snippet MCB MifareClassicBlock".st.et exec "Snippet MCBN MifareClassicBlockNumber".st.et exec "Snippet MCSN MifareClassicSectorNumber".st.et exec "Snippet MCK MifareClassicKey".st.et exec "Snippet MCKT MifareClassicKeyType".st.et exec "Snippet MD MIFARE_DESFIRE".st.et exec "Snippet MDA MifareDESFireAID".st.et exec "Snippet MDK MifareDESFireKey".st.et exec "Snippet MT MifareTag".st.et exec "Snippet MU MIFARE_ULTRALIGHT".st.et exec "Snippet MUP MifareUltralightPage".st.et exec "Snippet MUPN MifareUltralightPageNumber".st.et libfreefare-0.4.0/contrib/libutil/000077500000000000000000000000001225640673100170735ustar00rootroot00000000000000libfreefare-0.4.0/contrib/libutil/Makefile.am000066400000000000000000000002511225640673100211250ustar00rootroot00000000000000# $Id$ if WITH_DEBUG if !HAS_LIBUTIL noinst_LTLIBRARIES = libutil.la noinst_HEADERS = libutil.h libutil_la_SOURCES = hexdump.c endif # !HAS_LIBUTIL endif # WITH_DEBUG libfreefare-0.4.0/contrib/libutil/hexdump.c000066400000000000000000000060131225640673100207110ustar00rootroot00000000000000/*- * Copyright (c) 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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, this list of conditions and the following disclaimer. * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. * * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94 * $Id$ */ //#include //__FBSDID("$FreeBSD: stable/8/lib/libutil/hexdump.c 180161 2008-07-01 22:30:57Z jhb $"); #include #include #include void hexdump(const void *ptr, int length, const char *hdr, int flags) { int i, j, k; int cols; const unsigned char *cp; char delim; if ((flags & HD_DELIM_MASK) != 0) delim = (flags & HD_DELIM_MASK) >> 8; else delim = ' '; if ((flags & HD_COLUMN_MASK) != 0) cols = flags & HD_COLUMN_MASK; else cols = 16; cp = ptr; for (i = 0; i < length; i+= cols) { if (hdr != NULL) printf("%s", hdr); if ((flags & HD_OMIT_COUNT) == 0) printf("%04x ", i); if ((flags & HD_OMIT_HEX) == 0) { for (j = 0; j < cols; j++) { k = i + j; if (k < length) printf("%c%02x", delim, cp[k]); else printf(" "); } } if ((flags & HD_OMIT_CHARS) == 0) { printf(" |"); for (j = 0; j < cols; j++) { k = i + j; if (k >= length) printf(" "); else if (cp[k] >= ' ' && cp[k] <= '~') printf("%c", cp[k]); else printf("."); } printf("|"); } printf("\n"); } } libfreefare-0.4.0/contrib/libutil/libutil.h000066400000000000000000000043601225640673100207130ustar00rootroot00000000000000/* * Copyright (c) 1996 Peter Wemm . * All rights reserved. * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 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 BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * $FreeBSD: stable/8/lib/libutil/libutil.h 185548 2008-12-02 06:50:26Z peter $ * $Id$ */ #ifndef _LIBUTIL_H_ #define _LIBUTIL_H_ void hexdump(const void *ptr, int length, const char *hdr, int flags); /* hexdump(3) */ #define HD_COLUMN_MASK 0xff #define HD_DELIM_MASK 0xff00 #define HD_OMIT_COUNT (1 << 16) #define HD_OMIT_HEX (1 << 17) #define HD_OMIT_CHARS (1 << 18) #endif /* !_LIBUTIL_H_ */ libfreefare-0.4.0/contrib/win32/000077500000000000000000000000001225640673100163715ustar00rootroot00000000000000libfreefare-0.4.0/contrib/win32/err.h000066400000000000000000000021521225640673100173320ustar00rootroot00000000000000/*- * Copyright (C) 2011 Glenn Ergeerts. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #ifndef __ERR_H__ #define __ERR_H__ #include #define warnx(...) do { \ fprintf (stderr, __VA_ARGS__); \ fprintf (stderr, "\n"); \ } while (0) #define errx(code, ...) do { \ fprintf (stderr, __VA_ARGS__); \ fprintf (stderr, "\n"); \ exit (code); \ } while (0) /* FIXME: warn / err are supposed to display errno's message */ #define warn warnx #define err errx #endif /* !__ERR_H__ */ libfreefare-0.4.0/debian/000077500000000000000000000000001225640673100152115ustar00rootroot00000000000000libfreefare-0.4.0/debian/.gitignore000066400000000000000000000001321225640673100171750ustar00rootroot00000000000000*.debhelper *.log *.substvars autoreconf.after autoreconf.before files libfreefare*/ tmp/ libfreefare-0.4.0/debian/README.Debian000066400000000000000000000002671225640673100172570ustar00rootroot00000000000000libfreefare for Debian ---------------------- -- neomilium Wed, 18 May 2011 12:20:45 +0200 libfreefare-0.4.0/debian/README.source000066400000000000000000000003111225640673100173630ustar00rootroot00000000000000libfreefare for Debian ---------------------- libfreefare-0.4.0/debian/changelog000066400000000000000000000011061225640673100170610ustar00rootroot00000000000000libfreefare (0.4.0-0) unstable; urgency=low * New upstream version -- Romuald Conty Wed, 4 Dec 2013 10:42:42 +0100 libfreefare (0.3.4-0) unstable; urgency=low * New upstream version -- Romuald Conty Thu, 1 Mar 2012 18:42:42 +0200 libfreefare (0.3.3-0) unstable; urgency=low * New upstream version -- Romuald Conty Thu, 1 Mar 2012 16:42:42 +0200 libfreefare (0.3.1-0) unstable; urgency=low * Initial debian package release -- Romuald Conty Wed, 18 May 2011 12:42:42 +0200 libfreefare-0.4.0/debian/compat000066400000000000000000000000021225640673100164070ustar00rootroot000000000000007 libfreefare-0.4.0/debian/control000066400000000000000000000031321225640673100166130ustar00rootroot00000000000000Source: libfreefare Section: libs Priority: extra Maintainer: Romuald Conty Build-Depends: debhelper (>= 7.0.50~), dh-autoreconf, libnfc-dev (>= 1.7.0~rc7), pkg-config, libssl-dev Standards-Version: 3.9.1 Homepage: http://code.google.com/p/nfc-tools/wiki/libfreefare Vcs-Svn: http://nfc-tools.googlecode.com/svn/trunk/libfreefare Vcs-Browser: http://code.google.com/p/nfc-tools/source/browse/#svn/trunk/libfreefare Package: libfreefare0 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: MIFARE card manipulations library The libfreefare project aims to provide a convenient API for MIFARE card manipulations. This package contains the libfreefare library. Package: libfreefare-dev Section: libdevel Architecture: any Depends: ${misc:Depends}, libfreefare0 (= ${binary:Version}), libnfc-dev Description: MIFARE card manipulations library (development files) The libfreefare project aims to provide a convenient API for MIFARE card manipulations. This package contains development files. Package: libfreefare-bin Section: utils Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, libfreefare0 (>= ${binary:Version}) Description: MIFARE card manipulations binaries The libfreefare project aims to provide a convenient API for MIFARE card manipulations. This package includes some binaries that are useful for development purposes. Package: libfreefare-doc Architecture: all Section: doc Depends: ${misc:Depends} Description: documentation for libfreefare The libfreefare project aims to provide a convenient API for MIFARE card manipulations. libfreefare-0.4.0/debian/copyright000066400000000000000000000017211225640673100171450ustar00rootroot00000000000000Format: http://dep.debian.net/deps/dep5 Upstream-Name: libfreefare Source: http://code.google.com/p/nfc-tools/wiki/libfreefare Files: * Copyright: 2010-2011, Romain Tartière , Romuald Conty License: LGPL-3 This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3 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, see . . On Debian GNU/Linux systems, the complete text of the GNU General Public License, version 3, can be found in the file /usr/share/common-licenses/LGPL-3. libfreefare-0.4.0/debian/libfreefare-bin.install000066400000000000000000000000341225640673100216120ustar00rootroot00000000000000debian/tmp/usr/bin/mifare-* libfreefare-0.4.0/debian/libfreefare-dev.install000066400000000000000000000001671225640673100216270ustar00rootroot00000000000000debian/tmp/usr/include/freefare.h debian/tmp/usr/lib/libfreefare.{a,la,so} debian/tmp/usr/lib/pkgconfig/libfreefare.pc libfreefare-0.4.0/debian/libfreefare-doc.install000066400000000000000000000000401225640673100216040ustar00rootroot00000000000000debian/tmp/usr/share/man/man3/* libfreefare-0.4.0/debian/libfreefare0.install000066400000000000000000000000441225640673100211250ustar00rootroot00000000000000debian/tmp/usr/lib/libfreefare.so.* libfreefare-0.4.0/debian/rules000077500000000000000000000013511225640673100162710ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # # Modified to make a template file for a multi-binary package with separated # build-arch and build-indep targets by Bill Allombert 2001 # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # This has to be exported to make some magic below work. export DH_OPTIONS override_dh_auto_configure: dh_auto_configure -- --without-cutter %: dh --with autoreconf $@ libfreefare-0.4.0/debian/source/000077500000000000000000000000001225640673100165115ustar00rootroot00000000000000libfreefare-0.4.0/debian/source/format000066400000000000000000000000141225640673100177170ustar00rootroot000000000000003.0 (quilt) libfreefare-0.4.0/debian/watch000066400000000000000000000002261225640673100162420ustar00rootroot00000000000000# See uscan(1) for format # Compulsory line, this is a version 3 file version=3 http://googlecode.debian.net/p/nfc-tools/libfreefare-(.*)\.tar\.gz libfreefare-0.4.0/examples/000077500000000000000000000000001225640673100156055ustar00rootroot00000000000000libfreefare-0.4.0/examples/CMakeLists.txt000066400000000000000000000014061225640673100203460ustar00rootroot00000000000000# $Id$ set(EXAMPLES-SOURCES mifare-classic-format mifare-classic-read-ndef mifare-classic-write-ndef mifare-desfire-access mifare-desfire-create-ndef mifare-desfire-format mifare-desfire-info mifare-desfire-read-ndef mifare-desfire-write-ndef mifare-desfire-ev1-configure-ats mifare-desfire-ev1-configure-default-key mifare-desfire-ev1-configure-random-uid mifare-ultralight-info ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../libfreefare) # Examples foreach(source ${EXAMPLES-SOURCES}) add_executable(${source} ${source}.c) target_link_libraries(${source} freefare) install(TARGETS ${source} RUNTIME DESTINATION bin COMPONENT examples) endforeach(source) libfreefare-0.4.0/examples/Makefile.am000066400000000000000000000046771225640673100176570ustar00rootroot00000000000000# $Id$ AM_CFLAGS = -I. -I$(top_srcdir)/libfreefare @LIBNFC_CFLAGS@ AM_LDFLAGS = @LIBNFC_LIBS@ bin_PROGRAMS = mifare-classic-format \ mifare-classic-write-ndef \ mifare-classic-read-ndef \ mifare-desfire-access \ mifare-desfire-create-ndef \ mifare-desfire-ev1-configure-ats \ mifare-desfire-ev1-configure-default-key \ mifare-desfire-ev1-configure-random-uid \ mifare-desfire-format \ mifare-desfire-info \ mifare-desfire-read-ndef \ mifare-desfire-write-ndef \ mifare-ultralight-info mifare_classic_format_SOURCES = mifare-classic-format.c mifare_classic_format_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_classic_read_ndef_SOURCES = mifare-classic-read-ndef.c mifare_classic_read_ndef_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_classic_write_ndef_SOURCES = mifare-classic-write-ndef.c mifare_classic_write_ndef_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_desfire_access_SOURCES = mifare-desfire-access.c mifare_desfire_access_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_desfire_create_ndef_SOURCES = mifare-desfire-create-ndef.c mifare_desfire_create_ndef_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_desfire_ev1_configure_ats_SOURCES = mifare-desfire-ev1-configure-ats.c mifare_desfire_ev1_configure_ats_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_desfire_ev1_configure_default_key_SOURCES = mifare-desfire-ev1-configure-default-key.c mifare_desfire_ev1_configure_default_key_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_desfire_ev1_configure_random_uid_SOURCES = mifare-desfire-ev1-configure-random-uid.c mifare_desfire_ev1_configure_random_uid_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_desfire_format_SOURCES = mifare-desfire-format.c mifare_desfire_format_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_desfire_info_SOURCES = mifare-desfire-info.c mifare_desfire_info_LDADD = $(top_builddir)/libfreefare/libfreefare.la -lm mifare_desfire_read_ndef_SOURCES = mifare-desfire-read-ndef.c mifare_desfire_read_ndef_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_desfire_write_ndef_SOURCES = mifare-desfire-write-ndef.c mifare_desfire_write_ndef_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_ultralight_info_SOURCES = mifare-ultralight-info.c mifare_ultralight_info_LDADD = $(top_builddir)/libfreefare/libfreefare.la CLEANFILES= *.gcno libfreefare-0.4.0/examples/mifare-classic-format.c000066400000000000000000000160621225640673100221260ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * Copyright (C) 2012, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include "config.h" #include #include #include #include #include #include #include #define START_FORMAT_N "Formatting %d sectors [" #define DONE_FORMAT "] done.\n" MifareClassicKey default_keys[40]; MifareClassicKey default_keys_int[] = { { 0xff,0xff,0xff,0xff,0xff,0xff }, { 0xd3,0xf7,0xd3,0xf7,0xd3,0xf7 }, { 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5 }, { 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5 }, { 0x4d,0x3a,0x99,0xc3,0x51,0xdd }, { 0x1a,0x98,0x2c,0x7e,0x45,0x9a }, { 0xaa,0xbb,0xcc,0xdd,0xee,0xff }, { 0x00,0x00,0x00,0x00,0x00,0x00 } }; int format_mifare_classic_1k (MifareTag tag); int format_mifare_classic_4k (MifareTag tag); int try_format_sector (MifareTag tag, MifareClassicSectorNumber sector); static int at_block = 0; static int mod_block = 10; struct { bool fast; bool interactive; } format_options = { .fast = false, .interactive = true }; static void display_progress (void) { at_block++; if (0 == (at_block % mod_block)) { printf ("%d", at_block); fflush (stdout); } else { printf ("."); fflush (stdout); } } int format_mifare_classic_1k (MifareTag tag) { printf (START_FORMAT_N, 16); for (int sector = 0; sector < 16; sector++) { if (!try_format_sector (tag, sector)) return 0; } printf (DONE_FORMAT); return 1; } int format_mifare_classic_4k (MifareTag tag) { printf (START_FORMAT_N, 32 + 8); for (int sector = 0; sector < (32 + 8); sector++) { if (!try_format_sector (tag, sector)) return 0; } printf (DONE_FORMAT); return 1; } int try_format_sector (MifareTag tag, MifareClassicSectorNumber sector) { display_progress (); for (size_t i = 0; i < (sizeof (default_keys) / sizeof (MifareClassicKey)); i++) { MifareClassicBlockNumber block = mifare_classic_sector_last_block (sector); if ((0 == mifare_classic_connect (tag)) && (0 == mifare_classic_authenticate (tag, block, default_keys[i], MFC_KEY_A))) { if (0 == mifare_classic_format_sector (tag, sector)) { mifare_classic_disconnect (tag); return 1; } else if (EIO == errno) { err (EXIT_FAILURE, "sector %d", sector); } mifare_classic_disconnect (tag); } if ((0 == mifare_classic_connect (tag)) && (0 == mifare_classic_authenticate (tag, block, default_keys[i], MFC_KEY_B))) { if (0 == mifare_classic_format_sector (tag, sector)) { mifare_classic_disconnect (tag); return 1; } else if (EIO == errno) { err (EXIT_FAILURE, "sector %d", sector); } mifare_classic_disconnect (tag); } } warnx ("No known authentication key for sector %d", sector); return 0; } static void usage(char *progname) { fprintf (stderr, "usage: %s [-fy] [keyfile]\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -f Fast format (only erase MAD)\n"); fprintf (stderr, " -y Do not ask for confirmation (dangerous)\n"); fprintf (stderr, " keyfile Use keys from dump in addition to internal default keys\n"); } int main(int argc, char *argv[]) { int ch; int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; while ((ch = getopt (argc, argv, "fhy")) != -1) { switch (ch) { case 'f': format_options.fast = true; break; case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': format_options.interactive = false; break; default: usage(argv[0]); exit (EXIT_FAILURE); } } // Remaining args, if any, are in argv[optind .. (argc-1)] memcpy(default_keys, default_keys_int, sizeof(default_keys_int)); if ((argc - optind) > 0) { int i, rc; char kbuffer[1024] = {0}; memset ( kbuffer, 0, sizeof kbuffer); FILE *fp = fopen(argv[optind], "rb"); if (fp == NULL) errx(EXIT_FAILURE, "Unable to open file"); for (i = 0; (rc = getc(fp)) != EOF && i < 1024; kbuffer[i++] = rc) { } fclose(fp); i = sizeof(default_keys_int) / 6; for(int s = 0; s<16; s++) { int startblock = s * 4; int pos_a = (startblock + 3) * 16; int pos_b = (startblock + 3) * 16 + 10; memcpy((default_keys + i++), kbuffer + pos_a, 6); memcpy((default_keys + i++), kbuffer + pos_b, 6); } } nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; d < device_count; d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing Mifare Classic tag."); } for (int i = 0; (!error) && tags[i]; i++) { switch (freefare_get_tag_type (tags[i])) { case CLASSIC_1K: case CLASSIC_4K: break; default: continue; } char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; printf ("Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); bool format = true; if (format_options.interactive) { printf ("Format [yN] "); fgets (buffer, BUFSIZ, stdin); format = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { printf ("\n"); } if (format) { enum mifare_tag_type tt = freefare_get_tag_type (tags[i]); at_block = 0; if (format_options.fast) { printf (START_FORMAT_N, (tt == CLASSIC_1K) ? 1 : 2); if (!try_format_sector (tags[i], 0x00)) break; if (tt == CLASSIC_4K) if (!try_format_sector (tags[i], 0x10)) break; printf (DONE_FORMAT); continue; } switch (tt) { case CLASSIC_1K: mod_block = 4; if (!format_mifare_classic_1k (tags[i])) error = 1; break; case CLASSIC_4K: mod_block = 10; if (!format_mifare_classic_4k (tags[i])) error = 1; break; default: /* Keep compiler quiet */ break; } } free (tag_uid); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } libfreefare-0.4.0/examples/mifare-classic-read-ndef.c000066400000000000000000000147301225640673100224630ustar00rootroot00000000000000/*- * Copyright (C) 2011, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id $ */ /* * This implementation was written based on information provided by the * following documents: * * Mifare Std as NFC Forum Enabled, * Extensions for Mifare standard 1k/4k as NFC Forum Enable Tag * Application note * Revision 1.1 — 21 August 2007 * * NXP Type MF1K/4K Tag Operation, NXP Semiconductors [ANNFC1K4K] * Application Note * Revision 1.1 — 21 August 2007 * Document Identifier 130410 */ #include "config.h" #include #include #include #include #include #include #include #include #define MIN(a,b) ((a < b) ? a: b) struct { bool interactive; } read_options = { .interactive = true }; static void usage(char *progname) { fprintf (stderr, "usage: %s -o FILE\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -y Do not ask for confirmation\n"); fprintf (stderr, " -o Extract NDEF message if available in FILE\n"); } int main(int argc, char *argv[]) { int error = 0; nfc_device *device = NULL; MifareTag *tags = NULL; Mad mad; int ch; char *ndef_output = NULL; while ((ch = getopt (argc, argv, "hyo:")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': read_options.interactive = false; break; case 'o': ndef_output = optarg; break; case '?': if (optopt == 'o') fprintf (stderr, "Option -%c requires an argument.\n", optopt); default: usage (argv[0]); exit (EXIT_FAILURE); } } if (ndef_output == NULL) { usage (argv[0]); exit (EXIT_FAILURE); } FILE* message_stream = NULL; FILE* ndef_stream = NULL; if ((strlen (ndef_output) == 1) && (ndef_output[0] == '-')) { message_stream = stderr; ndef_stream = stdout; } else { message_stream = stdout; ndef_stream = fopen(ndef_output, "wb"); if (!ndef_stream) { fprintf (stderr, "Could not open file %s.\n", ndef_output); exit (EXIT_FAILURE); } } nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count= nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; d < device_count; d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing MIFARE classic tag."); } for (int i = 0; (!error) && tags[i]; i++) { switch (freefare_get_tag_type (tags[i])) { case CLASSIC_1K: case CLASSIC_4K: break; default: continue; } char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; fprintf (message_stream, "Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); bool read_ndef = true; if (read_options.interactive) { fprintf (message_stream, "Read NDEF [yN] "); fgets (buffer, BUFSIZ, stdin); read_ndef = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { fprintf (message_stream, "\n"); } if (read_ndef) { // NFCForum card has a MAD, load it. if (0 == mifare_classic_connect (tags[i])) { } else { nfc_perror (device, "mifare_classic_connect"); error = EXIT_FAILURE; goto error; } if ((mad = mad_read (tags[i]))) { // Dump the NFCForum application using MAD information uint8_t buffer[4096]; ssize_t len; if ((len = mifare_application_read (tags[i], mad, mad_nfcforum_aid, buffer, sizeof(buffer), mifare_classic_nfcforum_public_key_a, MFC_KEY_A)) != -1) { uint8_t tlv_type; uint16_t tlv_data_len; uint8_t * tlv_data; uint8_t * pbuffer = buffer; decode_tlv: tlv_data = tlv_decode (pbuffer, &tlv_type, &tlv_data_len); switch (tlv_type) { case 0x00: fprintf (message_stream, "NFC Forum application contains a \"NULL TLV\", Skipping...\n"); // According to [ANNFC1K4K], we skip this Tag to read further TLV blocks. pbuffer += tlv_record_length(pbuffer, NULL, NULL); if (pbuffer >= buffer + sizeof(buffer)) { error = EXIT_FAILURE; goto error; } goto decode_tlv; break; case 0x03: fprintf (message_stream, "NFC Forum application contains a \"NDEF Message TLV\".\n"); break; case 0xFD: fprintf (message_stream, "NFC Forum application contains a \"Proprietary TLV\", Skipping...\n"); // According to [ANNFC1K4K], we can skip this TLV to read further TLV blocks. pbuffer += tlv_record_length(pbuffer, NULL, NULL); if (pbuffer >= buffer + sizeof(buffer)) { error = EXIT_FAILURE; goto error; } goto decode_tlv; break; case 0xFE: fprintf (stderr, "NFC Forum application contains a \"Terminator TLV\", no available data.\n"); error = EXIT_FAILURE; goto error; break; default: fprintf (stderr, "NFC Forum application contains an invalid TLV.\n"); error = EXIT_FAILURE; goto error; break; } if (fwrite (tlv_data, 1, tlv_data_len, ndef_stream) != tlv_data_len) { fprintf (stderr, "Could not write to file.\n"); error = EXIT_FAILURE; goto error; } free (tlv_data); } else { fprintf (stderr, "No NFC Forum application.\n"); error = EXIT_FAILURE; goto error; } } else { fprintf (stderr, "No MAD detected.\n"); error = EXIT_FAILURE; goto error; } free (mad); } error: free (tag_uid); } fclose (ndef_stream); freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } libfreefare-0.4.0/examples/mifare-classic-write-ndef.c000066400000000000000000000315121225640673100226770ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include "config.h" #include #include #include #include #include #include #include #include #define MIN(a,b) ((a < b) ? a: b) MifareClassicKey default_keys[] = { { 0xff,0xff,0xff,0xff,0xff,0xff }, { 0xd3,0xf7,0xd3,0xf7,0xd3,0xf7 }, { 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5 }, { 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5 }, { 0x4d,0x3a,0x99,0xc3,0x51,0xdd }, { 0x1a,0x98,0x2c,0x7e,0x45,0x9a }, { 0xaa,0xbb,0xcc,0xdd,0xee,0xff }, { 0x00,0x00,0x00,0x00,0x00,0x00 } }; struct mifare_classic_key_and_type { MifareClassicKey key; MifareClassicKeyType type; }; struct { bool interactive; } write_options = { .interactive = true }; const MifareClassicKey default_keyb = { 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7 }; const uint8_t ndef_default_msg[33] = { 0xd1, 0x02, 0x1c, 0x53, 0x70, 0x91, 0x01, 0x09, 0x54, 0x02, 0x65, 0x6e, 0x4c, 0x69, 0x62, 0x6e, 0x66, 0x63, 0x51, 0x01, 0x0b, 0x55, 0x03, 0x6c, 0x69, 0x62, 0x6e, 0x66, 0x63, 0x2e, 0x6f, 0x72, 0x67 }; uint8_t *ndef_msg; size_t ndef_msg_len; static int search_sector_key (MifareTag tag, MifareClassicSectorNumber sector, MifareClassicKey *key, MifareClassicKeyType *key_type) { MifareClassicBlockNumber block = mifare_classic_sector_last_block (sector); /* * FIXME: We should not assume that if we have full access to trailer block * we also have a full access to data blocks. */ mifare_classic_disconnect (tag); for (size_t i = 0; i < (sizeof (default_keys) / sizeof (MifareClassicKey)); i++) { if ((0 == mifare_classic_connect (tag)) && (0 == mifare_classic_authenticate (tag, block, default_keys[i], MFC_KEY_A))) { if ((1 == mifare_classic_get_trailer_block_permission (tag, block, MCAB_WRITE_KEYA, MFC_KEY_A)) && (1 == mifare_classic_get_trailer_block_permission (tag, block, MCAB_WRITE_ACCESS_BITS, MFC_KEY_A)) && (1 == mifare_classic_get_trailer_block_permission (tag, block, MCAB_WRITE_KEYB, MFC_KEY_A))) { memcpy (key, &default_keys[i], sizeof (MifareClassicKey)); *key_type = MFC_KEY_A; return 1; } } mifare_classic_disconnect (tag); if ((0 == mifare_classic_connect (tag)) && (0 == mifare_classic_authenticate (tag, block, default_keys[i], MFC_KEY_B))) { if ((1 == mifare_classic_get_trailer_block_permission (tag, block, MCAB_WRITE_KEYA, MFC_KEY_B)) && (1 == mifare_classic_get_trailer_block_permission (tag, block, MCAB_WRITE_ACCESS_BITS, MFC_KEY_B)) && (1 == mifare_classic_get_trailer_block_permission (tag, block, MCAB_WRITE_KEYB, MFC_KEY_B))) { memcpy (key, &default_keys[i], sizeof (MifareClassicKey)); *key_type = MFC_KEY_B; return 1; } } mifare_classic_disconnect (tag); } warnx ("No known authentication key for sector 0x%02x\n", sector); return 0; } static int fix_mad_trailer_block (nfc_device *device, MifareTag tag, MifareClassicSectorNumber sector, MifareClassicKey key, MifareClassicKeyType key_type) { MifareClassicBlock block; mifare_classic_trailer_block (&block, mad_public_key_a, 0x0, 0x1, 0x1, 0x6, 0x00, default_keyb); if (mifare_classic_authenticate (tag, mifare_classic_sector_last_block (sector), key, key_type) < 0) { nfc_perror (device, "fix_mad_trailer_block mifare_classic_authenticate"); return -1; } if (mifare_classic_write (tag, mifare_classic_sector_last_block (sector), block) < 0) { nfc_perror (device, "mifare_classic_write"); return -1; } return 0; } static void usage(char *progname) { fprintf (stderr, "usage: %s -i FILE\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -y Do not ask for confirmation\n"); fprintf (stderr, " -i Use FILE as NDEF message to write on card (\"-\" = stdin)\n"); } int main(int argc, char *argv[]) { int error = 0; nfc_device *device = NULL; MifareTag *tags = NULL; Mad mad; MifareClassicKey transport_key = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int ch; char *ndef_input = NULL; while ((ch = getopt (argc, argv, "hyi:")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': write_options.interactive = false; break; case 'i': ndef_input = optarg; break; case '?': if (optopt == 'i') fprintf (stderr, "Option -%c requires an argument.\n", optopt); default: usage (argv[0]); exit (EXIT_FAILURE); } } if (ndef_input == NULL) { ndef_msg = (uint8_t*)ndef_default_msg; ndef_msg_len = sizeof(ndef_default_msg); } else { FILE* ndef_stream = NULL; if ((strlen (ndef_input) == 1) && (ndef_input[0] == '-')) { // FIXME stdin as input have to be readed and buffered in ndef_msg ndef_stream = stdin; fprintf (stderr, "stdin as NDEF is not implemented"); exit (EXIT_FAILURE); } else { ndef_stream = fopen(ndef_input, "rb"); if (!ndef_stream) { fprintf (stderr, "Could not open file %s.\n", ndef_input); exit (EXIT_FAILURE); } fseek(ndef_stream, 0L, SEEK_END); ndef_msg_len = ftell(ndef_stream); fseek(ndef_stream, 0L, SEEK_SET); if (!(ndef_msg = malloc (ndef_msg_len))) { err (EXIT_FAILURE, "malloc"); } if (fread (ndef_msg, 1, ndef_msg_len, ndef_stream) != ndef_msg_len) { fprintf (stderr, "Could not read NDEF from file: %s\n", ndef_input); fclose (ndef_stream); free (ndef_msg); exit (EXIT_FAILURE); } fclose (ndef_stream); } } printf ("NDEF file is %zu bytes long.\n", ndef_msg_len); struct mifare_classic_key_and_type *card_write_keys; if (!(card_write_keys = malloc (40 * sizeof (*card_write_keys)))) { err (EXIT_FAILURE, "malloc"); } nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; d < device_count; d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing MIFARE classic tag."); } for (int i = 0; (!error) && tags[i]; i++) { switch (freefare_get_tag_type (tags[i])) { case CLASSIC_1K: case CLASSIC_4K: break; default: continue; } char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; printf ("Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); bool write_ndef = true; if (write_options.interactive) { printf ("Write NDEF [yN] "); fgets (buffer, BUFSIZ, stdin); write_ndef = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { printf ("\n"); } for (int n = 0; n < 40; n++) { memcpy(card_write_keys[n].key, transport_key, sizeof (transport_key)); card_write_keys[n].type = MFC_KEY_A; } if (write_ndef) { switch (freefare_get_tag_type (tags[i])) { case CLASSIC_4K: if (!search_sector_key (tags[i], 0x10, &(card_write_keys[0x10].key), &(card_write_keys[0x10].type))) { error = 1; goto error; } /* fallthrough */ case CLASSIC_1K: if (!search_sector_key (tags[i], 0x00, &(card_write_keys[0x00].key), &(card_write_keys[0x00].type))) { error = 1; goto error; } break; default: /* Keep compiler quiet */ break; } if (!error) { /* Ensure the auth key is always a B one. If not, change it! */ switch (freefare_get_tag_type (tags[i])) { case CLASSIC_4K: if (card_write_keys[0x10].type != MFC_KEY_B) { if( 0 != fix_mad_trailer_block (device, tags[i], 0x10, card_write_keys[0x10].key, card_write_keys[0x10].type)) { error = 1; goto error; } memcpy (&(card_write_keys[0x10].key), &default_keyb, sizeof (MifareClassicKey)); card_write_keys[0x10].type = MFC_KEY_B; } /* fallthrough */ case CLASSIC_1K: if (card_write_keys[0x00].type != MFC_KEY_B) { if( 0 != fix_mad_trailer_block (device, tags[i], 0x00, card_write_keys[0x00].key, card_write_keys[0x00].type)) { error = 1; goto error; } memcpy (&(card_write_keys[0x00].key), &default_keyb, sizeof (MifareClassicKey)); card_write_keys[0x00].type = MFC_KEY_B; } break; default: /* Keep compiler quiet */ break; } } size_t encoded_size; uint8_t *tlv_data = tlv_encode (3, ndef_msg, ndef_msg_len, &encoded_size); /* * At his point, we should have collected all information needed to * succeed. */ // If the card already has a MAD, load it. if ((mad = mad_read (tags[i]))) { // If our application already exists, erase it. MifareClassicSectorNumber *sectors, *p; sectors = p = mifare_application_find (mad, mad_nfcforum_aid); if (sectors) { while (*p) { if (mifare_classic_authenticate (tags[i], mifare_classic_sector_last_block(*p), default_keyb, MFC_KEY_B) < 0) { nfc_perror (device, "mifare_classic_authenticate"); error = 1; goto error; } if (mifare_classic_format_sector (tags[i], *p) < 0) { nfc_perror (device, "mifare_classic_format_sector"); error = 1; goto error; } p++; } } free (sectors); mifare_application_free (mad, mad_nfcforum_aid); } else { // Create a MAD and mark unaccessible sectors in the card if (!(mad = mad_new ((freefare_get_tag_type (tags[i]) == CLASSIC_4K) ? 2 : 1))) { perror ("mad_new"); error = 1; goto error; } MifareClassicSectorNumber max_s; switch (freefare_get_tag_type (tags[i])) { case CLASSIC_1K: max_s = 15; break; case CLASSIC_4K: max_s = 39; break; default: /* Keep compiler quiet */ break; } // Mark unusable sectors as so for (size_t s = max_s; s; s--) { if (s == 0x10) continue; if (!search_sector_key (tags[i], s, &(card_write_keys[s].key), &(card_write_keys[s].type))) { mad_set_aid (mad, s, mad_defect_aid); } else if ((memcmp (card_write_keys[s].key, transport_key, sizeof (transport_key)) != 0) && (card_write_keys[s].type != MFC_KEY_A)) { // Revert to transport configuration if (mifare_classic_format_sector (tags[i], s) < 0) { nfc_perror (device, "mifare_classic_format_sector"); error = 1; goto error; } } } } MifareClassicSectorNumber *sectors = mifare_application_alloc (mad, mad_nfcforum_aid, encoded_size); if (!sectors) { nfc_perror (device, "mifare_application_alloc"); error = EXIT_FAILURE; goto error; } if (mad_write (tags[i], mad, card_write_keys[0x00].key, card_write_keys[0x10].key) < 0) { nfc_perror (device, "mad_write"); error = EXIT_FAILURE; goto error; } int s = 0; while (sectors[s]) { MifareClassicBlockNumber block = mifare_classic_sector_last_block (sectors[s]); MifareClassicBlock block_data; mifare_classic_trailer_block (&block_data, mifare_classic_nfcforum_public_key_a, 0x0, 0x0, 0x0, 0x6, 0x40, default_keyb); if (mifare_classic_authenticate (tags[i], block, card_write_keys[sectors[s]].key, card_write_keys[sectors[s]].type) < 0) { nfc_perror (device, "mifare_classic_authenticate"); error = EXIT_FAILURE; goto error; } if (mifare_classic_write (tags[i], block, block_data) < 0) { nfc_perror (device, "mifare_classic_write"); error = EXIT_FAILURE; goto error; } s++; } if ((ssize_t) encoded_size != mifare_application_write (tags[i], mad, mad_nfcforum_aid, tlv_data, encoded_size, default_keyb, MCAB_WRITE_KEYB)) { nfc_perror (device, "mifare_application_write"); error = EXIT_FAILURE; goto error; } free (sectors); free (tlv_data); free (mad); } error: free (tag_uid); } if (ndef_msg != ndef_default_msg) free (ndef_msg); freefare_free_tags (tags); nfc_close (device); } free (card_write_keys); nfc_exit (context); exit (error); } libfreefare-0.4.0/examples/mifare-desfire-access.c000066400000000000000000000076541225640673100221060ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include #include #include uint8_t key_data_null[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int main(int argc, char *argv[]) { int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; if (argc > 1) errx (EXIT_FAILURE, "usage: %s", argv[0]); nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; d < device_count; d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing tags."); } for (int i = 0; (!error) && tags[i]; i++) { if (DESFIRE != freefare_get_tag_type (tags[i])) continue; int res; char *tag_uid = freefare_get_tag_uid (tags[i]); res = mifare_desfire_connect (tags[i]); if (res < 0) { warnx ("Can't connect to Mifare DESFire target."); error = EXIT_FAILURE; break; } MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data_null); res = mifare_desfire_authenticate (tags[i], 0, key); if (res < 0) errx (EXIT_FAILURE, "Authentication on master application failed"); MadAid mad_aid = { 0x12, 0x34 }; MifareDESFireAID aid = mifare_desfire_aid_new_with_mad_aid (mad_aid, 0x5); res = mifare_desfire_create_application (tags[i], aid, 0xFF, 0x1); if (res < 0) errx (EXIT_FAILURE, "Application creation failed"); res = mifare_desfire_select_application (tags[i], aid); if (res < 0) errx (EXIT_FAILURE, "Application selection failed"); res = mifare_desfire_authenticate (tags[i], 0, key); if (res < 0) errx (EXIT_FAILURE, "Authentication on application failed"); res = mifare_desfire_create_std_data_file (tags[i], 1, MDCM_ENCIPHERED, 0x0000, 20); if (res < 0) errx (EXIT_FAILURE, "File creation failed"); const char *s= "Hello World"; res = mifare_desfire_write_data (tags[i], 1, 0, strlen (s), s); if (res < 0) errx (EXIT_FAILURE, "File write failed"); char buffer[20]; res = mifare_desfire_read_data (tags[i], 1, 0, 0, buffer); if (res < 0) errx (EXIT_FAILURE, "File read failed"); res = mifare_desfire_select_application (tags[i], NULL); if (res < 0) errx (EXIT_FAILURE, "Master application selection failed"); res = mifare_desfire_authenticate (tags[i], 0, key); if (res < 0) errx (EXIT_FAILURE, "Authentication on master application failed"); res = mifare_desfire_format_picc (tags[i]); if (res < 0) errx (EXIT_FAILURE, "PICC format failed"); mifare_desfire_key_free (key); free (tag_uid); free (aid); mifare_desfire_disconnect (tags[i]); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } /* main() */ libfreefare-0.4.0/examples/mifare-desfire-create-ndef.c000066400000000000000000000320071225640673100230100ustar00rootroot00000000000000/*- * Copyright (C) 2010, Audrey Diacre. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include "config.h" #include #include #include #include #include /* * This example was written based on information provided by the * following documents: * * AN11004 Mifare DESFire as Type 4 Tag * NFC Forum Type 4 Tag Extensions for Mifare DESFire * Rev. 1.1 - 21 August 2007 * Rev. 2.2 - 4 January 2012 * */ uint8_t key_data_picc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t key_data_app[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; struct { bool interactive; } create_options = { .interactive = true }; static void usage(char *progname) { fprintf (stderr, "This application turns Mifare DESFire targets into NFC Forum Type 4 Tags.\n"); fprintf (stderr, "usage: %s [-y] [-K 11223344AABBCCDD]\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -y Do not ask for confirmation\n"); fprintf (stderr, " -K Provide another PICC key than the default one\n"); } int main(int argc, char *argv[]) { int ch; int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; while ((ch = getopt (argc, argv, "hyK:")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': create_options.interactive = false; break; case 'K': if (strlen(optarg) != 16) { usage(argv[0]); exit (EXIT_FAILURE); } uint64_t n = strtoull(optarg, NULL, 16); int i; for (i=7; i>=0; i--) { key_data_picc[i] = (uint8_t) n; n >>= 8; } break; default: usage(argv[0]); exit (EXIT_FAILURE); } } // Remaining args, if any, are in argv[optind .. (argc-1)] nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; d < device_count; d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing tags."); } for (int i = 0; (!error) && tags[i]; i++) { if (DESFIRE != freefare_get_tag_type (tags[i])) continue; char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; int res; res = mifare_desfire_connect (tags[i]); if (res < 0) { warnx ("Can't connect to Mifare DESFire target."); error = EXIT_FAILURE; break; } // We've to track DESFire version as NDEF mapping is different struct mifare_desfire_version_info info; res = mifare_desfire_get_version (tags[i], &info); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_get_version"); error = 1; break; } printf ("Found %s with UID %s and software v%d.%d\n", freefare_get_tag_friendly_name (tags[i]), tag_uid, info.software.version_major, info.software.version_minor); bool create_ndef = true; int ndef_mapping; switch (info.software.version_major) { case 0: ndef_mapping = 1; break; case 1: ndef_mapping = 2; default: // newer version? let's assume it supports latest mapping too ndef_mapping = 2; } if (create_options.interactive) { printf ("Create NDEF app v%d [yN] ", ndef_mapping); fgets (buffer, BUFSIZ, stdin); create_ndef = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { printf ("\n"); } if (create_ndef) { /* Initialised Formatting Procedure. See section 6.5.1 and 8.1 of Mifare DESFire as Type 4 Tag document*/ // Send Mifare DESFire Select Application with AID equal to 000000h to select the PICC level res = mifare_desfire_select_application(tags[i], NULL); if (res < 0) errx (EXIT_FAILURE, "Application selection failed"); MifareDESFireKey key_picc; MifareDESFireKey key_app; key_picc = mifare_desfire_des_key_new_with_version (key_data_picc); key_app = mifare_desfire_des_key_new_with_version (key_data_app); // Authentication with PICC master key MAY be needed to issue ChangeKeySettings command res = mifare_desfire_authenticate (tags[i], 0, key_picc); if (res < 0) errx (EXIT_FAILURE, "Authentication with PICC master key failed"); MifareDESFireAID aid; if (ndef_mapping == 1) { uint8_t key_settings; uint8_t max_keys; mifare_desfire_get_key_settings(tags[i], &key_settings,&max_keys); if ((key_settings & 0x08) == 0x08){ // Send Mifare DESFire ChangeKeySetting to change the PICC master key settings into : // bit7-bit4 equal to 0000b // bit3 equal to Xb, the configuration of the PICC master key MAY be changeable or frozen // bit2 equal to 0b, CreateApplication and DeleteApplication commands are allowed with PICC master key authentication // bit1 equal to 0b, GetApplicationIDs, and GetKeySettings are allowed with PICC master key authentication // bit0 equal to Xb, PICC masterkey MAY be frozen or changeable res = mifare_desfire_change_key_settings (tags[i],0x09); if (res < 0) errx (EXIT_FAILURE, "ChangeKeySettings failed"); } // Mifare DESFire Create Application with AID equal to EEEE10h, key settings equal to 0x09, NumOfKeys equal to 01h aid = mifare_desfire_aid_new(0xEEEE10); res = mifare_desfire_create_application (tags[i], aid, 0x09, 1); if (res < 0) errx (EXIT_FAILURE, "Application creation failed. Try mifare-desfire-format before running %s.", argv[0]); // Mifare DESFire SelectApplication (Select previously creates application) res = mifare_desfire_select_application(tags[i], aid); if (res < 0) errx (EXIT_FAILURE, "Application selection failed"); free (aid); // Authentication with NDEF Tag Application master key (Authentication with key 0) res = mifare_desfire_authenticate (tags[i], 0, key_app); if (res < 0) errx (EXIT_FAILURE, "Authentication with NDEF Tag Application master key failed"); // Mifare DESFire ChangeKeySetting with key settings equal to 00001001b res = mifare_desfire_change_key_settings (tags[i],0x09); if (res < 0) errx (EXIT_FAILURE, "ChangeKeySettings failed"); // Mifare DESFire CreateStdDataFile with FileNo equal to 03h (CC File DESFire FID), ComSet equal to 00h, // AccesRights equal to E000h, File Size bigger equal to 00000Fh res = mifare_desfire_create_std_data_file(tags[i],0x03,MDCM_PLAIN,0xE000,0x00000F); if (res < 0) errx (EXIT_FAILURE, "CreateStdDataFile failed"); // Mifare DESFire WriteData to write the content of the CC File with CClEN equal to 000Fh, // Mapping Version equal to 10h,MLe equal to 003Bh, MLc equal to 0034h, and NDEF File Control TLV // equal to T =04h, L=06h, V=E1 04 (NDEF ISO FID=E104h) 0E E0 (NDEF File size =3808 Bytes) 00 (free read access) // 00 free write access uint8_t capability_container_file_content[15] = { 0x00, 0x0F, // CCLEN: Size of this capability container.CCLEN values are between 000Fh and FFFEh 0x10, // Mapping version 0x00, 0x3B, // MLe: Maximum data size that can be read using a single ReadBinary command. MLe = 000Fh-FFFFh 0x00, 0x34, // MLc: Maximum data size that can be sent using a single UpdateBinary command. MLc = 0001h-FFFFh 0x04, 0x06, // T & L of NDEF File Control TLV, followed by 6 bytes of V: 0xE1, 0x04, // File Identifier of NDEF File 0x0E, 0xE0, // Maximum NDEF File size of 3808 bytes 0x00, // free read access 0x00 // free write acces }; res = mifare_desfire_write_data(tags[i],0x03,0,sizeof(capability_container_file_content),capability_container_file_content); if (res>0){ // Mifare DESFire CreateStdDataFile with FileNo equal to 04h (NDEF FileDESFire FID), CmmSet equal to 00h, AccessRigths // equal to EEE0h, FileSize equal to 000EE0h (3808 Bytes) res = mifare_desfire_create_std_data_file(tags[i],0x04,MDCM_PLAIN,0xEEE0,0x000EE0); if (res < 0) errx (EXIT_FAILURE, "CreateStdDataFile failed"); } else { errx (EXIT_FAILURE, "Write CC file content failed"); } } else if (ndef_mapping == 2) { // Mifare DESFire Create Application with AID equal to 000001h, key settings equal to 0x0F, NumOfKeys equal to 01h, // 2 bytes File Identifiers supported, File-ID equal to E110h aid = mifare_desfire_aid_new(0x000001); uint8_t app[] = {0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01}; res = mifare_desfire_create_application_iso (tags[i], aid, 0x0F, 0x21, 0, 0xE110, app, sizeof (app)); if (res < 0) errx (EXIT_FAILURE, "Application creation failed. Try mifare-desfire-format before running %s.", argv[0]); // Mifare DESFire SelectApplication (Select previously creates application) res = mifare_desfire_select_application(tags[i], aid); if (res < 0) errx (EXIT_FAILURE, "Application selection failed"); free (aid); // Authentication with NDEF Tag Application master key (Authentication with key 0) res = mifare_desfire_authenticate (tags[i], 0, key_app); if (res < 0) errx (EXIT_FAILURE, "Authentication with NDEF Tag Application master key failed"); // Mifare DESFire CreateStdDataFile with FileNo equal to 01h (DESFire FID), ComSet equal to 00h, // AccesRights equal to E000h, File Size bigger equal to 00000Fh, ISO File ID equal to E103h res = mifare_desfire_create_std_data_file_iso(tags[i],0x01,MDCM_PLAIN,0xE000,0x00000F,0xE103); if (res < 0) errx (EXIT_FAILURE, "CreateStdDataFileIso failed"); // Mifare DESFire WriteData to write the content of the CC File with CClEN equal to 000Fh, // Mapping Version equal to 20h,MLe equal to 003Bh, MLc equal to 0034h, and NDEF File Control TLV // equal to T =04h, L=06h, V=E1 04 (NDEF ISO FID=E104h) 0xNNNN (NDEF File size = 0x0800/0x1000/0x1E00 bytes) // 00 (free read access) 00 free write access uint8_t capability_container_file_content[15] = { 0x00, 0x0F, // CCLEN: Size of this capability container.CCLEN values are between 000Fh and FFFEh 0x20, // Mapping version 0x00, 0x3B, // MLe: Maximum data size that can be read using a single ReadBinary command. MLe = 000Fh-FFFFh 0x00, 0x34, // MLc: Maximum data size that can be sent using a single UpdateBinary command. MLc = 0001h-FFFFh 0x04, 0x06, // T & L of NDEF File Control TLV, followed by 6 bytes of V: 0xE1, 0x04, // File Identifier of NDEF File 0x04, 0x00, // Maximum NDEF File size of 1024 bytes 0x00, // free read access 0x00 // free write acces }; uint16_t ndefmaxsize = 0x0800; uint16_t announcedsize = 1 << (info.software.storage_size >> 1); if (announcedsize >= 0x1000) ndefmaxsize = 0x1000; if (announcedsize >= 0x1E00) ndefmaxsize = 0x1E00; capability_container_file_content[11] = ndefmaxsize >> 8; capability_container_file_content[12] = ndefmaxsize & 0xFF; res = mifare_desfire_write_data(tags[i],0x01,0,sizeof(capability_container_file_content),capability_container_file_content); if (res>0){ // Mifare DESFire CreateStdDataFile with FileNo equal to 02h (DESFire FID), CmmSet equal to 00h, AccessRigths // equal to EEE0h, FileSize equal to ndefmaxsize (0x000800, 0x001000 or 0x001E00) res = mifare_desfire_create_std_data_file_iso(tags[i],0x02,MDCM_PLAIN,0xEEE0,ndefmaxsize, 0xE104); if (res < 0) errx (EXIT_FAILURE, "CreateStdDataFileIso failed"); } else { errx (EXIT_FAILURE, "Write CC file content failed"); } } mifare_desfire_key_free (key_picc); mifare_desfire_key_free (key_app); } mifare_desfire_disconnect (tags[i]); free (tag_uid); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } libfreefare-0.4.0/examples/mifare-desfire-ev1-configure-ats.c000066400000000000000000000110561225640673100240730ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include "config.h" #include #include #include #include #include #include #include uint8_t key_data_picc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // TODO: Allow to pass another ATS as option // Default Mifare DESFire ATS uint8_t new_ats[] = { 0x06, 0x75, 0x77, 0x81, 0x02, 0x80 }; struct { bool interactive; } configure_options = { .interactive = true }; static void usage(char *progname) { fprintf (stderr, "usage: %s [-y] [-K 11223344AABBCCDD]\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -y Do not ask for confirmation (dangerous)\n"); fprintf (stderr, " -K Provide another PICC key than the default one\n"); } int main(int argc, char *argv[]) { int ch; int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; while ((ch = getopt (argc, argv, "hyK:")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': configure_options.interactive = false; break; case 'K': if (strlen(optarg) != 16) { usage(argv[0]); exit (EXIT_FAILURE); } uint64_t n = strtoull(optarg, NULL, 16); int i; for (i=7; i>=0; i--) { key_data_picc[i] = (uint8_t) n; n >>= 8; } break; default: usage(argv[0]); exit (EXIT_FAILURE); } } // Remaining args, if any, are in argv[optind .. (argc-1)] nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; (!error) && (d < device_count); d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing Mifare DESFire tags."); } for (int i = 0; (!error) && tags[i]; i++) { if (DESFIRE != freefare_get_tag_type (tags[i])) continue; char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; int res; res = mifare_desfire_connect (tags[i]); if (res < 0) { warnx ("Can't connect to Mifare DESFire target."); error = EXIT_FAILURE; break; } // Make sure we've at least an EV1 version struct mifare_desfire_version_info info; res = mifare_desfire_get_version (tags[i], &info); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_get_version"); error = 1; break; } if (info.software.version_major < 1) { warnx ("Found old DESFire, skipping"); continue; } printf ("Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); bool do_it = true; if (configure_options.interactive) { printf ("Change ATS? [yN] "); fgets (buffer, BUFSIZ, stdin); do_it = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { printf ("\n"); } if (do_it) { MifareDESFireKey key_picc = mifare_desfire_des_key_new_with_version (key_data_picc); res = mifare_desfire_authenticate (tags[i], 0, key_picc); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_authenticate"); error = EXIT_FAILURE; break; } mifare_desfire_key_free (key_picc); res = mifare_desfire_set_ats (tags[i], new_ats); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_set_ats"); error = EXIT_FAILURE; break; } } mifare_desfire_disconnect (tags[i]); free (tag_uid); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } /* main() */ libfreefare-0.4.0/examples/mifare-desfire-ev1-configure-default-key.c000066400000000000000000000147061225640673100255230ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include "config.h" #include #include #include #include #include #include #include uint8_t null_key_data[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t new_key_data[8] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; #define NEW_KEY_VERSION 0x34 struct { bool interactive; } configure_options = { .interactive = true }; static void usage(char *progname) { fprintf (stderr, "usage: %s [-y]\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -y Do not ask for confirmation\n"); } int main(int argc, char *argv[]) { int ch; int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; while ((ch = getopt (argc, argv, "hy")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': configure_options.interactive = false; break; default: usage(argv[0]); exit (EXIT_FAILURE); } } // Remaining args, if any, are in argv[optind .. (argc-1)] nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; (!error) && (d < device_count); d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing Mifare DESFire tags."); } for (int i = 0; (!error) && tags[i]; i++) { if (DESFIRE != freefare_get_tag_type (tags[i])) continue; char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; int res; res = mifare_desfire_connect (tags[i]); if (res < 0) { warnx ("Can't connect to Mifare DESFire target."); error = EXIT_FAILURE; break; } // Make sure we've at least an EV1 version struct mifare_desfire_version_info info; res = mifare_desfire_get_version (tags[i], &info); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_get_version"); error = 1; break; } if (info.software.version_major < 1) { warnx ("Found old DESFire, skipping"); continue; } printf ("Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); bool do_it = true; if (configure_options.interactive) { printf ("Change default key? [yN] "); fgets (buffer, BUFSIZ, stdin); do_it = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { printf ("\n"); } if (do_it) { MifareDESFireKey default_key = mifare_desfire_des_key_new_with_version (null_key_data); res = mifare_desfire_authenticate (tags[i], 0, default_key); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_authenticate"); error = EXIT_FAILURE; break; } mifare_desfire_key_free (default_key); MifareDESFireKey new_key = mifare_desfire_des_key_new (new_key_data); mifare_desfire_key_set_version (new_key, NEW_KEY_VERSION); res = mifare_desfire_set_default_key (tags[i], new_key); free (new_key); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_set_default_key"); error = EXIT_FAILURE; break; } /* * Perform some tests to ensure the function actually worked * (it's hard to create a unit-test to do so). */ MifareDESFireAID aid = mifare_desfire_aid_new (0x112233); res = mifare_desfire_create_application (tags[i], aid, 0xFF, 1); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_create_application"); error = EXIT_FAILURE; break; } res = mifare_desfire_select_application (tags[i], aid); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_select_application"); error = EXIT_FAILURE; break; } uint8_t version; res = mifare_desfire_get_key_version (tags[i], 0, &version); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_get_key_version"); error = EXIT_FAILURE; break; } if (version != NEW_KEY_VERSION) { fprintf (stderr, "Wrong key version: %02x (expected %02x).\n", version, NEW_KEY_VERSION); error = EXIT_FAILURE; /* continue */ } new_key = mifare_desfire_des_key_new (new_key_data); res = mifare_desfire_authenticate (tags[i], 0, new_key); free (new_key); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_authenticate"); error = EXIT_FAILURE; break; } free (aid); /* Resetdefault settings */ res = mifare_desfire_select_application (tags[i], NULL); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_select_application"); error = EXIT_FAILURE; break; } default_key = mifare_desfire_des_key_new (null_key_data); res = mifare_desfire_authenticate (tags[i], 0, default_key); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_authenticate"); error = EXIT_FAILURE; break; } res = mifare_desfire_set_default_key (tags[i], default_key); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_set_default_key"); error = EXIT_FAILURE; break; } mifare_desfire_key_free (default_key); /* Wipeout the card */ res = mifare_desfire_format_picc (tags[i]); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_format_picc"); error = EXIT_FAILURE; break; } } mifare_desfire_disconnect (tags[i]); free (tag_uid); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } libfreefare-0.4.0/examples/mifare-desfire-ev1-configure-random-uid.c000066400000000000000000000126771225640673100253550ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include "config.h" #include #include #include #include #include #include #include uint8_t key_data_picc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; struct { bool interactive; } configure_options = { .interactive = true }; static void usage(char *progname) { fprintf (stderr, "usage: %s [-y] [-K 11223344AABBCCDD]\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -y Do not ask for confirmation (dangerous)\n"); fprintf (stderr, " -K Provide another PICC key than the default one\n"); } int main(int argc, char *argv[]) { int ch; int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; while ((ch = getopt (argc, argv, "hyK:")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': configure_options.interactive = false; break; case 'K': if (strlen(optarg) != 16) { usage(argv[0]); exit (EXIT_FAILURE); } uint64_t n = strtoull(optarg, NULL, 16); int i; for (i=7; i>=0; i--) { key_data_picc[i] = (uint8_t) n; n >>= 8; } break; default: usage(argv[0]); exit (EXIT_FAILURE); } } // Remaining args, if any, are in argv[optind .. (argc-1)] nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; (!error) && (d < device_count); d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing Mifare DESFire tags."); } for (int i = 0; (!error) && tags[i]; i++) { if (DESFIRE != freefare_get_tag_type (tags[i])) continue; char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; int res; res = mifare_desfire_connect (tags[i]); if (res < 0) { warnx ("Can't connect to Mifare DESFire target."); error = EXIT_FAILURE; break; } // Make sure we've at least an EV1 version struct mifare_desfire_version_info info; res = mifare_desfire_get_version (tags[i], &info); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_get_version"); error = 1; break; } if (info.software.version_major < 1) { warnx ("Found old DESFire, skipping"); continue; } printf ("Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); bool do_it = true; size_t tag_uid_len = strlen (tag_uid) / 2; switch (tag_uid_len) { case 7: // Regular UID if (configure_options.interactive) { printf ("Configure random UID (this cannot be undone) [yN] "); fgets (buffer, BUFSIZ, stdin); do_it = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { printf ("\n"); } if (do_it) { MifareDESFireKey key_picc = mifare_desfire_des_key_new_with_version (key_data_picc); res = mifare_desfire_authenticate (tags[i], 0, key_picc); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_authenticate"); error = EXIT_FAILURE; break; } mifare_desfire_key_free (key_picc); res = mifare_desfire_set_configuration (tags[i], false, true); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_set_configuration"); error = EXIT_FAILURE; break; } } break; case 4: // Random UID {} // Compilation fails if label is directly followed by the declaration rather than a statement MifareDESFireKey key_picc = mifare_desfire_des_key_new_with_version (key_data_picc); res = mifare_desfire_authenticate (tags[i], 0, key_picc); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_authenticate"); error = EXIT_FAILURE; break; } mifare_desfire_key_free (key_picc); char *old_tag_uid; res = mifare_desfire_get_card_uid (tags[i], &old_tag_uid); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_get_card_uid"); error = EXIT_FAILURE; break; } printf ("Old card UID: %s\n", old_tag_uid); free (old_tag_uid); break; default: // Should not happen warnx ("Unsupported UID length %d.", (int) tag_uid_len); error = EXIT_FAILURE; break; } mifare_desfire_disconnect (tags[i]); free (tag_uid); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } /* main() */ libfreefare-0.4.0/examples/mifare-desfire-format.c000066400000000000000000000112101225640673100221140ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include "config.h" #include #include #include #include #include #include #include uint8_t key_data_picc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; struct { bool interactive; } format_options = { .interactive = true }; static void usage(char *progname) { fprintf (stderr, "usage: %s [-y] [-K 11223344AABBCCDD]\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -y Do not ask for confirmation (dangerous)\n"); fprintf (stderr, " -K Provide another PICC key than the default one\n"); } int main(int argc, char *argv[]) { int ch; int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; while ((ch = getopt (argc, argv, "hyK:")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': format_options.interactive = false; break; case 'K': if (strlen(optarg) != 16) { usage(argv[0]); exit (EXIT_FAILURE); } uint64_t n = strtoull(optarg, NULL, 16); int i; for (i=7; i>=0; i--) { key_data_picc[i] = (uint8_t) n; n >>= 8; } break; default: usage(argv[0]); exit (EXIT_FAILURE); } } // Remaining args, if any, are in argv[optind .. (argc-1)] nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; (!error) && (d < device_count); d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing Mifare DESFire tags."); } for (int i = 0; (!error) && tags[i]; i++) { if (DESFIRE != freefare_get_tag_type (tags[i])) continue; char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; printf ("Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); bool format = true; if (format_options.interactive) { printf ("Format [yN] "); fgets (buffer, BUFSIZ, stdin); format = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { printf ("\n"); } if (format) { int res; res = mifare_desfire_connect (tags[i]); if (res < 0) { warnx ("Can't connect to Mifare DESFire target."); error = EXIT_FAILURE; break; } MifareDESFireKey key_picc = mifare_desfire_des_key_new_with_version (key_data_picc); res = mifare_desfire_authenticate (tags[i], 0, key_picc); if (res < 0) { warnx ("Can't authenticate on Mifare DESFire target."); error = EXIT_FAILURE; break; } mifare_desfire_key_free (key_picc); // Send Mifare DESFire ChangeKeySetting to change the PICC master key settings into : // bit7-bit4 equal to 0000b // bit3 equal to 1b, the configuration of the PICC master key MAY be changeable or frozen // bit2 equal to 1b, CreateApplication and DeleteApplication commands are allowed without PICC master key authentication // bit1 equal to 1b, GetApplicationIDs, and GetKeySettings are allowed without PICC master key authentication // bit0 equal to 1b, PICC masterkey MAY be frozen or changeable res = mifare_desfire_change_key_settings (tags[i],0x0F); if (res < 0) errx (EXIT_FAILURE, "ChangeKeySettings failed"); res = mifare_desfire_format_picc (tags[i]); if (res < 0) { warn ("Can't format PICC."); error = EXIT_FAILURE; break; } mifare_desfire_disconnect (tags[i]); } free (tag_uid); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } /* main() */ libfreefare-0.4.0/examples/mifare-desfire-info.c000066400000000000000000000124531225640673100215710ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include #include #include int main(int argc, char *argv[]) { int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; if (argc > 1) errx (EXIT_FAILURE, "usage: %s", argv[0]); nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; d < device_count; d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing tags."); } for (int i = 0; (!error) && tags[i]; i++) { if (DESFIRE != freefare_get_tag_type (tags[i])) continue; int res; char *tag_uid = freefare_get_tag_uid (tags[i]); struct mifare_desfire_version_info info; res = mifare_desfire_connect (tags[i]); if (res < 0) { warnx ("Can't connect to Mifare DESFire target."); error = 1; break; } res = mifare_desfire_get_version (tags[i], &info); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_get_version"); error = 1; break; } printf ("===> Version information for tag %s:\n", tag_uid); printf ("UID: 0x%02x%02x%02x%02x%02x%02x%02x\n", info.uid[0], info.uid[1], info.uid[2], info.uid[3], info.uid[4], info.uid[5], info.uid[6]); printf ("Batch number: 0x%02x%02x%02x%02x%02x\n", info.batch_number[0], info.batch_number[1], info.batch_number[2], info.batch_number[3], info.batch_number[4]); printf ("Production date: week %x, 20%02x\n", info.production_week, info.production_year); printf ("Hardware Information:\n"); printf (" Vendor ID: 0x%02x\n", info.hardware.vendor_id); printf (" Type: 0x%02x\n", info.hardware.type); printf (" Subtype: 0x%02x\n", info.hardware.subtype); printf (" Version: %d.%d\n", info.hardware.version_major, info.hardware.version_minor); printf (" Storage size: 0x%02x (%s%d bytes)\n", info.hardware.storage_size, (info.hardware.storage_size & 1) ? ">" : "=", 1 << (info.hardware.storage_size >> 1)); printf (" Protocol: 0x%02x\n", info.hardware.protocol); printf ("Software Information:\n"); printf (" Vendor ID: 0x%02x\n", info.software.vendor_id); printf (" Type: 0x%02x\n", info.software.type); printf (" Subtype: 0x%02x\n", info.software.subtype); printf (" Version: %d.%d\n", info.software.version_major, info.software.version_minor); printf (" Storage size: 0x%02x (%s%d bytes)\n", info.software.storage_size, (info.software.storage_size & 1) ? ">" : "=", 1 << (info.software.storage_size >> 1)); printf (" Protocol: 0x%02x\n", info.software.protocol); uint8_t settings; uint8_t max_keys; res = mifare_desfire_get_key_settings (tags[i], &settings, &max_keys); if (res == 0) { printf ("Master Key settings (0x%02x):\n", settings); printf (" 0x%02x configuration changeable;\n", settings & 0x08); printf (" 0x%02x PICC Master Key not required for create / delete;\n", settings & 0x04); printf (" 0x%02x Free directory list access without PICC Master Key;\n", settings & 0x02); printf (" 0x%02x Allow changing the Master Key;\n", settings & 0x01); } else if (AUTHENTICATION_ERROR == mifare_desfire_last_picc_error (tags[i])) { printf ("Master Key settings: LOCKED\n"); } else { freefare_perror (tags[i], "mifare_desfire_get_key_settings"); error = 1; break; } uint8_t version; mifare_desfire_get_key_version (tags[i], 0, &version); printf ("Master Key version: %d (0x%02x)\n", version, version); uint32_t size; res = mifare_desfire_free_mem (tags[i], &size); printf ("Free memory: "); if (0 == res) { printf ("%d bytes\n", size); } else { printf ("unknown\n"); } printf ("Use random UID: %s\n", (strlen (tag_uid) / 2 == 4) ? "yes" : "no"); free (tag_uid); mifare_desfire_disconnect (tags[i]); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } /* main() */ libfreefare-0.4.0/examples/mifare-desfire-read-ndef.c000066400000000000000000000211741225640673100224630ustar00rootroot00000000000000/*- * Copyright (C) 2010, Audrey Diacre. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include "config.h" #include #include #include #include #include /* * This example was written based on information provided by the * following documents: * * Mifare DESFire as Type 4 Tag * NFC Forum Type 4 Tag Extensions for Mifare DESFire * Rev. 1.1 - 21 August 2007 * Rev. 2.2 - 4 January 2012 * */ // Note that it is using specific Desfire commands, not ISO7816 NDEF Tag Type4 commands uint8_t key_data_app[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t *cc_data; uint8_t *ndef_msg; uint16_t ndef_msg_len; struct { bool interactive; } read_options = { .interactive = true }; static void usage(char *progname) { fprintf (stderr, "This application reads a NDEF payload from a Mifare DESFire formatted as NFC Forum Type 4 Tag.\n"); fprintf (stderr, "usage: %s [-y] -o FILE [-k 11223344AABBCCDD]\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -y Do not ask for confirmation\n"); fprintf (stderr, " -o Extract NDEF message if available in FILE\n"); fprintf (stderr, " -k Provide another NDEF Tag Application key than the default one\n"); } int main(int argc, char *argv[]) { int ch; int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; char *ndef_output = NULL; while ((ch = getopt (argc, argv, "hyo:k:")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': read_options.interactive = false; break; case 'o': ndef_output = optarg; break; case 'k': if (strlen(optarg) != 16) { usage(argv[0]); exit (EXIT_FAILURE); } uint64_t n = strtoull(optarg, NULL, 16); int i; for (i=7; i>=0; i--) { key_data_app[i] = (uint8_t) n; n >>= 8; } break; default: usage(argv[0]); exit (EXIT_FAILURE); } } // Remaining args, if any, are in argv[optind .. (argc-1)] if (ndef_output == NULL) { usage (argv[0]); exit (EXIT_FAILURE); } FILE* message_stream = NULL; FILE* ndef_stream = NULL; if ((strlen (ndef_output) == 1) && (ndef_output[0] == '-')) { message_stream = stderr; ndef_stream = stdout; } else { message_stream = stdout; ndef_stream = fopen(ndef_output, "wb"); if (!ndef_stream) { fprintf (stderr, "Could not open file %s.\n", ndef_output); exit (EXIT_FAILURE); } } nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; d < device_count; d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing tags."); } for (int i = 0; (!error) && tags[i]; i++) { if (DESFIRE != freefare_get_tag_type (tags[i])) continue; char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; fprintf (message_stream, "Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); bool read_ndef = true; if (read_options.interactive) { fprintf (message_stream, "Read NDEF [yN] "); fgets (buffer, BUFSIZ, stdin); read_ndef = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { fprintf (message_stream, "\n"); } if (read_ndef) { int res; res = mifare_desfire_connect (tags[i]); if (res < 0) { warnx ("Can't connect to Mifare DESFire target."); error = EXIT_FAILURE; break; } // We've to track DESFire version as NDEF mapping is different struct mifare_desfire_version_info info; res = mifare_desfire_get_version (tags[i], &info); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_get_version"); error = 1; break; } MifareDESFireKey key_app; key_app = mifare_desfire_des_key_new_with_version (key_data_app); // Mifare DESFire SelectApplication (Select application) MifareDESFireAID aid; if (info.software.version_major==0) aid = mifare_desfire_aid_new(0xEEEE10); else // There is no more relationship between DESFire AID and ISO AID... // Let's assume it's in AID 000001h as proposed in the spec aid = mifare_desfire_aid_new(0x000001); res = mifare_desfire_select_application(tags[i], aid); if (res < 0) errx (EXIT_FAILURE, "Application selection failed. Try mifare-desfire-create-ndef before running %s.", argv[0]); free (aid); // Authentication with NDEF Tag Application master key (Authentication with key 0) res = mifare_desfire_authenticate (tags[i], 0, key_app); if (res < 0) errx (EXIT_FAILURE, "Authentication with NDEF Tag Application master key failed"); // Read Capability Container file E103 uint8_t lendata[20]; // cf FIXME in mifare_desfire.c read_data() if (info.software.version_major==0) res = mifare_desfire_read_data (tags[i], 0x03, 0, 2, lendata); else // There is no more relationship between DESFire FID and ISO FileID... // Let's assume it's in FID 01h as proposed in the spec res = mifare_desfire_read_data (tags[i], 0x01, 0, 2, lendata); if (res < 0) errx (EXIT_FAILURE, "Read CC len failed"); uint16_t cclen = (((uint16_t) lendata[0]) << 8) + ((uint16_t) lendata[1]); if (cclen < 15) errx (EXIT_FAILURE, "CC too short IMHO"); if (!(cc_data = malloc(cclen+20))) // cf FIXME in mifare_desfire.c read_data() errx (EXIT_FAILURE, "malloc"); if (info.software.version_major==0) res = mifare_desfire_read_data (tags[i], 0x03, 0, cclen, cc_data); else res = mifare_desfire_read_data (tags[i], 0x01, 0, cclen, cc_data); if (res < 0) errx (EXIT_FAILURE, "Read CC data failed"); // Search NDEF File Control TLV uint8_t off = 7; while (((off+7) < cclen) && (cc_data[off] != 0x04)) { // Skip TLV off += cc_data[off+1] + 2; } if (off+7 >= cclen) errx (EXIT_FAILURE, "CC does not contain expected NDEF File Control TLV"); if (cc_data[off+2] != 0xE1) errx (EXIT_FAILURE, "Unknown NDEF File reference in CC"); uint8_t file_no; if (info.software.version_major==0) file_no = cc_data[off+3]; else // There is no more relationship between DESFire FID and ISO FileID... // Let's assume it's in FID 02h as proposed in the spec file_no = 2; uint16_t ndefmaxlen = (((uint16_t) cc_data[off+4]) << 8) + ((uint16_t) cc_data[off+5]); fprintf (message_stream, "Max NDEF size: %i bytes\n", ndefmaxlen); if (!(ndef_msg = malloc(ndefmaxlen+20))) // cf FIXME in mifare_desfire.c read_data() errx (EXIT_FAILURE, "malloc"); res = mifare_desfire_read_data (tags[i], file_no, 0, 2, lendata); if (res < 0) errx (EXIT_FAILURE, "Read NDEF len failed"); ndef_msg_len = (((uint16_t) lendata[0]) << 8) + ((uint16_t) lendata[1]); fprintf (message_stream, "NDEF size: %i bytes\n", ndef_msg_len); if (ndef_msg_len + 2 > ndefmaxlen) errx (EXIT_FAILURE, "Declared NDEF size larger than max NDEF size"); res = mifare_desfire_read_data (tags[i], file_no, 2, ndef_msg_len, ndef_msg); if (res < 0) errx (EXIT_FAILURE, "Read data failed"); if (fwrite (ndef_msg, 1, ndef_msg_len, ndef_stream) != ndef_msg_len) errx (EXIT_FAILURE, "Write to file failed"); free (cc_data); free (ndef_msg); mifare_desfire_key_free (key_app); mifare_desfire_disconnect (tags[i]); } free (tag_uid); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } libfreefare-0.4.0/examples/mifare-desfire-write-ndef.c000066400000000000000000000226621225640673100227050ustar00rootroot00000000000000/*- * Copyright (C) 2010, Audrey Diacre. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include "config.h" #include #include #include #include #include /* * This example was written based on information provided by the * following documents: * * Mifare DESFire as Type 4 Tag * NFC Forum Type 4 Tag Extensions for Mifare DESFire * Rev. 1.1 - 21 August 2007 * Rev. 2.2 - 4 January 2012 * */ uint8_t key_data_app[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // TODO: allow NDEF payload to be provided e.g. via an external file const uint8_t ndef_default_msg[33] = { 0xd1, 0x02, 0x1c, 0x53, 0x70, 0x91, 0x01, 0x09, 0x54, 0x02, 0x65, 0x6e, 0x4c, 0x69, 0x62, 0x6e, 0x66, 0x63, 0x51, 0x01, 0x0b, 0x55, 0x03, 0x6c, 0x69, 0x62, 0x6e, 0x66, 0x63, 0x2e, 0x6f, 0x72, 0x67 }; uint8_t *cc_data; uint8_t *ndef_msg; size_t ndef_msg_len; struct { bool interactive; } write_options = { .interactive = true }; static void usage(char *progname) { fprintf (stderr, "This application writes a NDEF payload into a Mifare DESFire formatted as NFC Forum Type 4 Tag.\n"); fprintf (stderr, "usage: %s [-y] -i FILE [-k 11223344AABBCCDD]\n", progname); fprintf (stderr, "\nOptions:\n"); fprintf (stderr, " -y Do not ask for confirmation\n"); fprintf (stderr, " -i Use FILE as NDEF message to write on card (\"-\" = stdin)\n"); fprintf (stderr, " -k Provide another NDEF Tag Application key than the default one\n"); } int main(int argc, char *argv[]) { int ch; int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; char *ndef_input = NULL; while ((ch = getopt (argc, argv, "hyi:k:")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; case 'y': write_options.interactive = false; break; case 'i': ndef_input = optarg; break; case 'k': if (strlen(optarg) != 16) { usage(argv[0]); exit (EXIT_FAILURE); } uint64_t n = strtoull(optarg, NULL, 16); int i; for (i=7; i>=0; i--) { key_data_app[i] = (uint8_t) n; n >>= 8; } break; default: usage(argv[0]); exit (EXIT_FAILURE); } } // Remaining args, if any, are in argv[optind .. (argc-1)] if (ndef_input == NULL) { ndef_msg_len = sizeof(ndef_default_msg) + 2; if (!(ndef_msg = malloc (ndef_msg_len))) { err (EXIT_FAILURE, "malloc"); } ndef_msg[0] = (uint8_t) ((ndef_msg_len - 2) >> 8); ndef_msg[1] = (uint8_t) (ndef_msg_len - 2); memcpy(ndef_msg + 2, ndef_default_msg, ndef_msg_len - 2); } else { FILE* ndef_stream = NULL; if ((strlen (ndef_input) == 1) && (ndef_input[0] == '-')) { // FIXME stdin as input have to be readed and buffered in ndef_msg ndef_stream = stdin; fprintf (stderr, "stdin as NDEF is not implemented"); exit (EXIT_FAILURE); } else { ndef_stream = fopen(ndef_input, "rb"); if (!ndef_stream) { fprintf (stderr, "Could not open file %s.\n", ndef_input); exit (EXIT_FAILURE); } fseek(ndef_stream, 0L, SEEK_END); ndef_msg_len = ftell(ndef_stream) + 2; fseek(ndef_stream, 0L, SEEK_SET); if (!(ndef_msg = malloc (ndef_msg_len))) { err (EXIT_FAILURE, "malloc"); } ndef_msg[0] = (uint8_t) ((ndef_msg_len - 2) >> 8); ndef_msg[1] = (uint8_t) (ndef_msg_len - 2); if (fread (ndef_msg + 2, 1, ndef_msg_len - 2, ndef_stream) != ndef_msg_len - 2) { fprintf (stderr, "Could not read NDEF from file: %s\n", ndef_input); fclose (ndef_stream); exit (EXIT_FAILURE); } fclose (ndef_stream); } } printf ("NDEF file is %zu bytes long.\n", ndef_msg_len); nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found."); for (size_t d = 0; d < device_count; d++) { device = nfc_open (context, devices[d]); if (!device) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } tags = freefare_get_tags (device); if (!tags) { nfc_close (device); errx (EXIT_FAILURE, "Error listing tags."); } for (int i = 0; (!error) && tags[i]; i++) { if (DESFIRE != freefare_get_tag_type (tags[i])) continue; char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; printf ("Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); bool write_ndef = true; if (write_options.interactive) { printf ("Write NDEF [yN] "); fgets (buffer, BUFSIZ, stdin); write_ndef = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } else { printf ("\n"); } if (write_ndef) { int res; res = mifare_desfire_connect (tags[i]); if (res < 0) { warnx ("Can't connect to Mifare DESFire target."); error = EXIT_FAILURE; break; } // We've to track DESFire version as NDEF mapping is different struct mifare_desfire_version_info info; res = mifare_desfire_get_version (tags[i], &info); if (res < 0) { freefare_perror (tags[i], "mifare_desfire_get_version"); error = 1; break; } MifareDESFireKey key_app; key_app = mifare_desfire_des_key_new_with_version (key_data_app); // Mifare DESFire SelectApplication (Select application) MifareDESFireAID aid; if (info.software.version_major==0) aid = mifare_desfire_aid_new(0xEEEE10); else // There is no more relationship between DESFire AID and ISO AID... // Let's assume it's in AID 000001h as proposed in the spec aid = mifare_desfire_aid_new(0x000001); res = mifare_desfire_select_application(tags[i], aid); if (res < 0) errx (EXIT_FAILURE, "Application selection failed. Try mifare-desfire-create-ndef before running %s.", argv[0]); free (aid); // Authentication with NDEF Tag Application master key (Authentication with key 0) res = mifare_desfire_authenticate (tags[i], 0, key_app); if (res < 0) errx (EXIT_FAILURE, "Authentication with NDEF Tag Application master key failed"); // Read Capability Container file E103 uint8_t lendata[20]; // cf FIXME in mifare_desfire.c read_data() if (info.software.version_major==0) res = mifare_desfire_read_data (tags[i], 0x03, 0, 2, lendata); else // There is no more relationship between DESFire FID and ISO FileID... // Let's assume it's in FID 01h as proposed in the spec res = mifare_desfire_read_data (tags[i], 0x01, 0, 2, lendata); if (res < 0) errx (EXIT_FAILURE, "Read CC len failed"); uint16_t cclen = (((uint16_t) lendata[0]) << 8) + ((uint16_t) lendata[1]); if (cclen < 15) errx (EXIT_FAILURE, "CC too short IMHO"); if (!(cc_data = malloc(cclen + 20))) // cf FIXME in mifare_desfire.c read_data() errx (EXIT_FAILURE, "malloc"); if (info.software.version_major==0) res = mifare_desfire_read_data (tags[i], 0x03, 0, cclen, cc_data); else res = mifare_desfire_read_data (tags[i], 0x01, 0, cclen, cc_data); if (res < 0) errx (EXIT_FAILURE, "Read CC data failed"); // Search NDEF File Control TLV uint8_t off = 7; while (((off+7) < cclen) && (cc_data[off] != 0x04)) { // Skip TLV off += cc_data[off+1] + 2; } if (off+7 >= cclen) errx (EXIT_FAILURE, "CC does not contain expected NDEF File Control TLV"); if (cc_data[off+2] != 0xE1) errx (EXIT_FAILURE, "Unknown NDEF File reference in CC"); uint8_t file_no; if (info.software.version_major==0) file_no = cc_data[off+3]; else // There is no more relationship between DESFire FID and ISO FileID... // Let's assume it's in FID 02h as proposed in the spec file_no = 2; uint16_t ndefmaxlen = (((uint16_t) cc_data[off+4]) << 8) + ((uint16_t) cc_data[off+5]); fprintf (stdout, "Max NDEF size: %i bytes\n", ndefmaxlen); if (ndef_msg_len > ndefmaxlen) errx (EXIT_FAILURE, "Supplied NDEF larger than max NDEF size"); //Mifare DESFire WriteData to write the content of the NDEF File with NLEN equal to NDEF Message length and NDEF Message res = mifare_desfire_write_data(tags[i], file_no, 0, ndef_msg_len, (uint8_t *) ndef_msg); if (res < 0) errx (EXIT_FAILURE, " Write data failed"); free(cc_data); mifare_desfire_key_free (key_app); mifare_desfire_disconnect (tags[i]); } free (tag_uid); } free (ndef_msg); freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit (error); } libfreefare-0.4.0/examples/mifare-ultralight-info.c000066400000000000000000000052571225640673100223330ustar00rootroot00000000000000/*- * Copyright (C) 2012, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include int main (int argc, char *argv[]) { int error = EXIT_SUCCESS; nfc_device *device = NULL; MifareTag *tags = NULL; if (argc > 1) errx (EXIT_FAILURE, "usage: %s", argv[0]); nfc_connstring devices[8]; size_t device_count; nfc_context *context; nfc_init (&context); if (context == NULL) errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); device_count = nfc_list_devices (context, devices, sizeof (devices) / sizeof (*devices)); if (device_count <= 0) errx (EXIT_FAILURE, "No NFC device found"); for (size_t d = 0; d < device_count; d++) { if (!(device = nfc_open (context, devices[d]))) { warnx ("nfc_open() failed."); error = EXIT_FAILURE; continue; } if (!(tags = freefare_get_tags (device))) { nfc_close (device); errx (EXIT_FAILURE, "Error listing tags."); } for (int i = 0; (!error) && tags[i]; i++) { switch (freefare_get_tag_type (tags[i])) { case ULTRALIGHT: case ULTRALIGHT_C: break; default: continue; } char *tag_uid = freefare_get_tag_uid (tags[i]); printf ("Tag with UID %s is a %s\n", tag_uid, freefare_get_tag_friendly_name (tags[i])); if (freefare_get_tag_type (tags[i]) == ULTRALIGHT_C) { MifareTag tag = tags[i]; int res; MifareDESFireKey key; uint8_t key1_3des_data[16] = { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }; key = mifare_desfire_3des_key_new (key1_3des_data); if (mifare_ultralight_connect (tag) < 0) errx (EXIT_FAILURE, "Error connecting to tag."); res = mifare_ultralightc_authenticate (tag, key); printf ("Authentication with default key: %s\n", res ? "fail" : "success"); mifare_desfire_key_free (key); mifare_ultralight_disconnect (tag); } free (tag_uid); } freefare_free_tags (tags); nfc_close (device); } nfc_exit (context); exit(error); } libfreefare-0.4.0/libfreefare.pc.in000066400000000000000000000004501225640673100171650ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libfreefare Description: MIFARE contactless tag access library Version: @VERSION@ URL: http://code.google.com/p/nfc-tools Requires: @PKG_CONFIG_REQUIRES@ Libs: -L${libdir} -lfreefare Cflags: -I${includedir} libfreefare-0.4.0/libfreefare/000077500000000000000000000000001225640673100162355ustar00rootroot00000000000000libfreefare-0.4.0/libfreefare/CMakeLists.txt000066400000000000000000000020311225640673100207710ustar00rootroot00000000000000# $Id$ set(LIBRARY_SOURCES freefare freefare_internal mad mifare_application mifare_classic mifare_desfire mifare_desfire_aid mifare_desfire_crypto mifare_desfire_error mifare_desfire_key mifare_ultralight tlv ../contrib/libutil/hexdump ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_library(freefare SHARED ${LIBRARY_SOURCES}) target_link_libraries(freefare ${LIBS}) set(installheaders freefare.h) install(FILES ${installheaders} DESTINATION ${INCLUDE_INSTALL_DIR}/freefare COMPONENT headers) if(WIN32) # On Windows the shared (runtime) library should be either in the same # directory as the excutables or in the path, we add it to same directory install(TARGETS freefare RUNTIME DESTINATION bin COMPONENT libraries) # At compile time we need the .LIB file, we place it in the lib directory install(TARGETS freefare ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT headers) else(WIN32) install(TARGETS freefare LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries) endif(WIN32) libfreefare-0.4.0/libfreefare/Makefile.am000066400000000000000000000161031225640673100202720ustar00rootroot00000000000000# $Id$ AM_CFLAGS = @LIBNFC_CFLAGS@ AM_LDFLAGS = @LIBNFC_LIBS@ lib_LTLIBRARIES = libfreefare.la libfreefare_la_SOURCES = freefare.c \ mifare_classic.c \ mifare_ultralight.c \ mifare_desfire.c \ mifare_desfire_aid.c \ mifare_desfire_crypto.c \ mifare_desfire_error.c \ mifare_desfire_key.c \ mad.c \ mifare_application.c \ tlv.c libfreefare_la_LIBADD = if WITH_DEBUG AM_CFLAGS += -DWITH_DEBUG if HAS_LIBUTIL libfreefare_la_LIBADD += -lutil else # HAS_LIBUTIL libfreefare_la_LIBADD += $(top_builddir)/contrib/libutil/libutil.la AM_CFLAGS += -I$(top_builddir)/contrib/libutil/ endif # !HAS_LIBUTIL endif # WITH_DEBUG libfreefare_la_HEADERS = freefare.h libfreefare_ladir = $(includedir) man_MANS = freefare.3 \ freefare_error.3 \ mad.3 \ mifare_application.3 \ mifare_classic.3 \ mifare_desfire.3 \ mifare_desfire_aid.3 \ mifare_desfire_key.3 \ mifare_ultralight.3 \ tlv.3 linkedman = \ freefare.3 freefare_free_tags.3 \ freefare.3 freefare_get_tag_friendly_name.3 \ freefare.3 freefare_get_tag_type.3 \ freefare.3 freefare_get_tag_uid.3 \ freefare.3 freefare_get_tags.3 \ freefare_error.3 freefare_perror.3 \ freefare_error.3 freefare_strerror.3 \ freefare_error.3 freefare_strerror_r.3 \ freefare_error.3 mifare_desfire_last_pcd_error.3 \ freefare_error.3 mifare_desfire_last_picc_error.3 \ mad.3 mad_free.3 \ mad.3 mad_get_aid.3 \ mad.3 mad_get_card_publisher_sector.3 \ mad.3 mad_get_version.3 \ mad.3 mad_new.3 \ mad.3 mad_read.3 \ mad.3 mad_set_aid.3 \ mad.3 mad_set_card_publisher_sector.3 \ mad.3 mad_set_version.3 \ mad.3 mad_write.3 \ mifare_application.3 mifare_application_alloc.3 \ mifare_application.3 mifare_application_find.3 \ mifare_application.3 mifare_application_free.3 \ mifare_application.3 mifare_application_read.3 \ mifare_application.3 mifare_application_write.3 \ mifare_classic.3 mifare_classic_authenticate.3 \ mifare_classic.3 mifare_classic_connect.3 \ mifare_classic.3 mifare_classic_decrement.3 \ mifare_classic.3 mifare_classic_disconnect.3 \ mifare_classic.3 mifare_classic_format_sector.3 \ mifare_classic.3 mifare_classic_get_data_block_permission.3 \ mifare_classic.3 mifare_classic_get_trailer_block_permission.3 \ mifare_classic.3 mifare_classic_get_uid.3 \ mifare_classic.3 mifare_classic_increment.3 \ mifare_classic.3 mifare_classic_init_value.3 \ mifare_classic.3 mifare_classic_read.3 \ mifare_classic.3 mifare_classic_read_value.3 \ mifare_classic.3 mifare_classic_restore.3 \ mifare_classic.3 mifare_classic_trailer_block.3 \ mifare_classic.3 mifare_classic_transfer.3 \ mifare_classic.3 mifare_classic_write.3 \ mifare_desfire.3 mifare_desfire_abort_transaction.3 \ mifare_desfire.3 mifare_desfire_authenticate.3 \ mifare_desfire.3 mifare_desfire_authenticate_aes.3 \ mifare_desfire.3 mifare_desfire_authenticate_iso.3 \ mifare_desfire.3 mifare_desfire_change_file_settings.3 \ mifare_desfire.3 mifare_desfire_change_key.3 \ mifare_desfire.3 mifare_desfire_change_key_settings.3 \ mifare_desfire.3 mifare_desfire_clear_record_file.3 \ mifare_desfire.3 mifare_desfire_commit_transaction.3 \ mifare_desfire.3 mifare_desfire_connect.3 \ mifare_desfire.3 mifare_desfire_create_application.3 \ mifare_desfire.3 mifare_desfire_create_backup_data_file.3 \ mifare_desfire.3 mifare_desfire_create_cyclic_record_file.3 \ mifare_desfire.3 mifare_desfire_create_linear_record_file.3 \ mifare_desfire.3 mifare_desfire_create_std_data_file.3 \ mifare_desfire.3 mifare_desfire_create_value_file.3 \ mifare_desfire.3 mifare_desfire_credit.3 \ mifare_desfire.3 mifare_desfire_credit_ex.3 \ mifare_desfire.3 mifare_desfire_debit.3 \ mifare_desfire.3 mifare_desfire_debit_ex.3 \ mifare_desfire.3 mifare_desfire_delete_application.3 \ mifare_desfire.3 mifare_desfire_delete_file.3 \ mifare_desfire.3 mifare_desfire_disconnect.3 \ mifare_desfire.3 mifare_desfire_format_picc.3 \ mifare_desfire.3 mifare_desfire_free_application_ids.3 \ mifare_desfire.3 mifare_desfire_free_mem.3 \ mifare_desfire.3 mifare_desfire_get_application_ids.3 \ mifare_desfire.3 mifare_desfire_get_card_uid.3 \ mifare_desfire.3 mifare_desfire_get_file_ids.3 \ mifare_desfire.3 mifare_desfire_get_file_settings.3 \ mifare_desfire.3 mifare_desfire_get_key_settings.3 \ mifare_desfire.3 mifare_desfire_get_key_version.3 \ mifare_desfire.3 mifare_desfire_get_value.3 \ mifare_desfire.3 mifare_desfire_get_value_ex.3 \ mifare_desfire.3 mifare_desfire_get_version.3 \ mifare_desfire.3 mifare_desfire_limited_credit.3 \ mifare_desfire.3 mifare_desfire_limited_credit_ex.3 \ mifare_desfire.3 mifare_desfire_read_data.3 \ mifare_desfire.3 mifare_desfire_read_data_ex.3 \ mifare_desfire.3 mifare_desfire_read_records.3 \ mifare_desfire.3 mifare_desfire_read_records_ex.3 \ mifare_desfire.3 mifare_desfire_select_application.3 \ mifare_desfire.3 mifare_desfire_set_ats.3 \ mifare_desfire.3 mifare_desfire_set_configuration.3 \ mifare_desfire.3 mifare_desfire_set_default_key.3 \ mifare_desfire.3 mifare_desfire_write_data.3 \ mifare_desfire.3 mifare_desfire_write_data_ex.3 \ mifare_desfire.3 mifare_desfire_write_record.3 \ mifare_desfire.3 mifare_desfire_write_record_ex.3 \ mifare_desfire_aid.3 mifare_desfire_aid_get_aid.3 \ mifare_desfire_aid.3 mifare_desfire_aid_new.3 \ mifare_desfire_aid.3 mifare_desfire_aid_new_with_mad_aid.3 \ mifare_desfire_key.3 mifare_desfire_3des_key_new.3 \ mifare_desfire_key.3 mifare_desfire_3des_key_new_with_version.3 \ mifare_desfire_key.3 mifare_desfire_3k3des_key_new.3 \ mifare_desfire_key.3 mifare_desfire_3k3des_key_new_with_version.3 \ mifare_desfire_key.3 mifare_desfire_aes_key_new.3 \ mifare_desfire_key.3 mifare_desfire_aes_key_new_with_version.3 \ mifare_desfire_key.3 mifare_desfire_des_key_new.3 \ mifare_desfire_key.3 mifare_desfire_des_key_new_with_version.3 \ mifare_desfire_key.3 mifare_desfire_key_free.3 \ mifare_desfire_key.3 mifare_desfire_key_get_version.3 \ mifare_desfire_key.3 mifare_desfire_key_set_version.3 \ mifare_ultralight.3 mifare_ultralight_connect.3 \ mifare_ultralight.3 mifare_ultralight_disconnect.3 \ mifare_ultralight.3 mifare_ultralight_get_uid.3 \ mifare_ultralight.3 mifare_ultralight_read.3 \ mifare_ultralight.3 mifare_ultralight_write.3 \ mifare_ultralight.3 mifare_ultralightc_authenticate.3 \ tlv.3 tlv_decode.3 \ tlv.3 tlv_encode.3 install-data-hook: (cd $(DESTDIR)$(man3dir); for i in `echo $(linkedman) | xargs -n2 echo | awk '{print $$2}'`; do rm -f $$i; done; echo $(linkedman) | xargs -n2 $(LN_S)) uninstall-hook: (cd $(DESTDIR)$(man3dir); for i in `echo $(linkedman) | xargs -n2 echo | awk '{print $$2}'`; do rm -f $$i; done;) EXTRA_DIST = freefare_internal.h $(man_MANS) CLEANFILES = *.gcno libfreefare-0.4.0/libfreefare/freefare.3000066400000000000000000000105731225640673100201060ustar00rootroot00000000000000.\" Copyright (C) 2010 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id$ .\" .Dd March 30, 2010 .Dt FREEFARE 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm freefare_get_tags , .Nm freefare_get_tag_type , .Nm freefare_get_tag_friendly_name , .Nm freefare_get_tag_uid , .Nm freefare_free_tag , .Nm freefare_free_tags .Nd Generic target manipulation functions .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft "MifareTag *" .Fn freefare_get_tags "nfc_device_t *device" .Bd -literal enum mifare_tag_type { ULTRALIGHT, CLASSIC_1K, CLASSIC_4K }; .Ed .Ft "enum mifare_tag_type" .Fn freefare_get_tag_type "MifareTag tag" .Ft "const char *" .Fn freefare_get_tag_friendly_name "MifareTag tag" .Ft "char *" .Fn freefare_get_tag_uid "MifareTag tag" .Ft "void" .Fn freefare_free_tag "MifareTag tags" .Ft "void" .Fn freefare_free_tags "MifareTag *tags" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION The .Fn freefare_* family of functions allow agnostic access to the .Vt MifareTag present on a given NFC device. .Pp .Fn freefare_get_tags returns a NULL-terminated list of .Vt MifareTag present on .Vt device . This list has to be freed after usage by either: .Bl -hyphen .It calling the .Fn freefare_free_tags function. All tags in the list are automatically freed; .It calling the .Fn freefare_free_tag function to free each tag in the list individually, and freeing the list itself using the .Fn free function. .El .Pp Because of the nature of the target detection process, any previously detected target will be in an inconsistent state after a call to .Fn freefare_get_tags . It is the programmer's responsibility to free these targets before calling the .Fn freefare_get_tags function. .Pp Information about a given .Vt MifareTag can be gathered using the .Fn freefare_get_tag_type , .Fn freefare_get_tag_uid and .Fn freefare_get_tag_friendly_name functions. .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES Unless stated otherwise, all functions return a value greater than or equal to .Va 0 on success or .Va -1 on failure. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr free 3 , .Xr mifare_classic 3 , .Xr mifare_ultralight 3 .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@blogreen.org .An Romuald Conty Aq romuald@libnfc.org libfreefare-0.4.0/libfreefare/freefare.c000066400000000000000000000157371225640673100201750ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include "freefare_internal.h" #define MAX_CANDIDATES 16 #define NXP_MANUFACTURER_CODE 0x04 struct supported_tag supported_tags[] = { { CLASSIC_1K, "Mifare Classic 1k", 0x08, 0, 0, { 0x00 }, NULL }, { CLASSIC_1K, "Infineon Mifare Classic 1k", 0x88, 0, 0, { 0x00 }, NULL }, { CLASSIC_4K, "Mifare Classic 4k", 0x18, 0, 0, { 0x00 }, NULL }, { CLASSIC_4K, "Mifare Classic 4k (Emulated)", 0x38, 0, 0, { 0x00 }, NULL }, { DESFIRE, "Mifare DESFire", 0x20, 5, 4, { 0x75, 0x77, 0x81, 0x02 /*, 0xXX */ }, NULL}, { ULTRALIGHT_C, "Mifare UltraLightC", 0x00, 0, 0, { 0x00 }, is_mifare_ultralightc_on_reader }, { ULTRALIGHT, "Mifare UltraLight", 0x00, 0, 0, { 0x00 }, NULL }, }; /* * Automagically allocate a MifareTag given a device and target info. */ MifareTag freefare_tag_new (nfc_device *device, nfc_iso14443a_info nai) { bool found = false; struct supported_tag *tag_info; MifareTag tag; /* Ensure the target is supported */ for (size_t i = 0; i < sizeof (supported_tags) / sizeof (struct supported_tag); i++) { if (((nai.szUidLen == 4) || (nai.abtUid[0] == NXP_MANUFACTURER_CODE)) && (nai.btSak == supported_tags[i].SAK) && (!supported_tags[i].ATS_min_length || ((nai.szAtsLen >= supported_tags[i].ATS_min_length) && (0 == memcmp (nai.abtAts, supported_tags[i].ATS, supported_tags[i].ATS_compare_length)))) && ((supported_tags[i].check_tag_on_reader == NULL) || supported_tags[i].check_tag_on_reader(device, nai))) { tag_info = &(supported_tags[i]); found = true; break; } } if (!found) return NULL; /* Allocate memory for the found MIFARE target */ switch (tag_info->type) { case CLASSIC_1K: case CLASSIC_4K: tag = mifare_classic_tag_new (); break; case DESFIRE: tag = mifare_desfire_tag_new (); break; case ULTRALIGHT: case ULTRALIGHT_C: tag = mifare_ultralight_tag_new (); break; } if (!tag) return NULL; /* * Initialize common fields * (Target specific fields are initialized in mifare_*_tag_new()) */ tag->device = device; tag->info = nai; tag->active = 0; tag->tag_info = tag_info; return tag; } /* * MIFARE card common functions * * The following functions send NFC commands to the initiator to prepare * communication with a MIFARE card, and perform required cleannups after using * the targets. */ /* * Get a list of the MIFARE targets near to the provided NFC initiator. * * The list has to be freed using the freefare_free_tags() function. */ MifareTag * freefare_get_tags (nfc_device *device) { MifareTag *tags = NULL; int tag_count = 0; nfc_initiator_init(device); // Drop the field for a while nfc_device_set_property_bool(device,NP_ACTIVATE_FIELD,false); // Configure the CRC and Parity settings nfc_device_set_property_bool(device,NP_HANDLE_CRC,true); nfc_device_set_property_bool(device,NP_HANDLE_PARITY,true); nfc_device_set_property_bool(device,NP_AUTO_ISO14443_4,true); // Enable field so more power consuming cards can power themselves up nfc_device_set_property_bool(device,NP_ACTIVATE_FIELD,true); // Poll for a ISO14443A (MIFARE) tag nfc_target candidates[MAX_CANDIDATES]; int candidates_count; nfc_modulation modulation = { .nmt = NMT_ISO14443A, .nbr = NBR_106 }; if ((candidates_count = nfc_initiator_list_passive_targets(device, modulation, candidates, MAX_CANDIDATES)) < 0) return NULL; tags = malloc(sizeof (void *)); if(!tags) return NULL; tags[0] = NULL; for (int c = 0; c < candidates_count; c++) { MifareTag t; if ((t = freefare_tag_new(device, candidates[c].nti.nai))) { /* (Re)Allocate memory for the found MIFARE targets array */ MifareTag *p = realloc (tags, (tag_count + 2) * sizeof (MifareTag)); if (p) tags = p; else return tags; // FAIL! Return what has been found so far. tags[tag_count++] = t; tags[tag_count] = NULL; } } return tags; } /* * Returns the type of the provided tag. */ enum mifare_tag_type freefare_get_tag_type (MifareTag tag) { return tag->tag_info->type; } /* * Returns the friendly name of the provided tag. */ const char * freefare_get_tag_friendly_name (MifareTag tag) { return tag->tag_info->friendly_name; } /* * Returns the UID of the provided tag. */ char * freefare_get_tag_uid (MifareTag tag) { char *res = malloc (2 * tag->info.szUidLen + 1); for (size_t i =0; i < tag->info.szUidLen; i++) snprintf (res + 2*i, 3, "%02x", tag->info.abtUid[i]); return res; } /* * Free the provided tag. */ void freefare_free_tag (MifareTag tag) { if (tag) { switch (tag->tag_info->type) { case CLASSIC_1K: case CLASSIC_4K: mifare_classic_tag_free (tag); break; case DESFIRE: mifare_desfire_tag_free (tag); break; case ULTRALIGHT: case ULTRALIGHT_C: mifare_ultralight_tag_free (tag); break; } } } const char * freefare_strerror (MifareTag tag) { const char *p = "Unknown error"; if (nfc_device_get_last_error (tag->device) < 0) { p = nfc_strerror (tag->device); } else { if (tag->tag_info->type == DESFIRE) { if (MIFARE_DESFIRE (tag)->last_pcd_error) { p = mifare_desfire_error_lookup (MIFARE_DESFIRE (tag)->last_pcd_error); } else if (MIFARE_DESFIRE (tag)->last_picc_error) { p = mifare_desfire_error_lookup (MIFARE_DESFIRE (tag)->last_picc_error); } } } return p; } int freefare_strerror_r (MifareTag tag, char *buffer, size_t len) { return (snprintf (buffer, len, "%s", freefare_strerror (tag)) < 0) ? -1 : 0; } void freefare_perror (MifareTag tag, const char *string) { fprintf (stderr, "%s: %s\n", string, freefare_strerror (tag)); } /* * Free the provided tag list. */ void freefare_free_tags (MifareTag *tags) { if (tags) { for (int i=0; tags[i]; i++) { freefare_free_tag(tags[i]); } free (tags); } } /* * Low-level API */ void * memdup (const void *p, const size_t n) { void *res; if ((res = malloc (n))) { memcpy (res, p, n); } return res; } libfreefare-0.4.0/libfreefare/freefare.h000066400000000000000000000445161225640673100201770ustar00rootroot00000000000000/*- * Copyright (C) 2009, 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #ifndef __FREEFARE_H__ #define __FREEFARE_H__ #include #include #include #ifdef __cplusplus extern "C" { #endif // __cplusplus enum mifare_tag_type { ULTRALIGHT, ULTRALIGHT_C, // MINI, CLASSIC_1K, CLASSIC_4K, // PLUS_S2K, // PLUS_S4K, // PLUS_X2K, // PLUS_X4K, DESFIRE }; struct mifare_tag; typedef struct mifare_tag *MifareTag; struct mifare_desfire_key; typedef struct mifare_desfire_key *MifareDESFireKey; typedef uint8_t MifareUltralightPageNumber; typedef unsigned char MifareUltralightPage[4]; MifareTag *freefare_get_tags (nfc_device *device); MifareTag freefare_tag_new (nfc_device *device, nfc_iso14443a_info nai); enum mifare_tag_type freefare_get_tag_type (MifareTag tag); const char *freefare_get_tag_friendly_name (MifareTag tag); char *freefare_get_tag_uid (MifareTag tag); void freefare_free_tag (MifareTag tag); void freefare_free_tags (MifareTag *tags); const char *freefare_strerror (MifareTag tag); int freefare_strerror_r (MifareTag tag, char *buffer, size_t len); void freefare_perror (MifareTag tag, const char *string); int mifare_ultralight_connect (MifareTag tag); int mifare_ultralight_disconnect (MifareTag tag); int mifare_ultralight_read (MifareTag tag, const MifareUltralightPageNumber page, MifareUltralightPage *data); int mifare_ultralight_write (MifareTag tag, const MifareUltralightPageNumber page, const MifareUltralightPage data); int mifare_ultralightc_authenticate (MifareTag tag, const MifareDESFireKey key); bool is_mifare_ultralightc_on_reader (nfc_device *device, nfc_iso14443a_info nai); typedef unsigned char MifareClassicBlock[16]; typedef uint8_t MifareClassicSectorNumber; typedef unsigned char MifareClassicBlockNumber; typedef enum { MFC_KEY_A, MFC_KEY_B } MifareClassicKeyType; typedef unsigned char MifareClassicKey[6]; /* NFC Forum public key */ extern const MifareClassicKey mifare_classic_nfcforum_public_key_a; int mifare_classic_connect (MifareTag tag); int mifare_classic_disconnect (MifareTag tag); int mifare_classic_authenticate (MifareTag tag, const MifareClassicBlockNumber block, const MifareClassicKey key, const MifareClassicKeyType key_type); int mifare_classic_read (MifareTag tag, const MifareClassicBlockNumber block, MifareClassicBlock *data); int mifare_classic_init_value (MifareTag tag, const MifareClassicBlockNumber block, const int32_t value, const MifareClassicBlockNumber adr); int mifare_classic_read_value (MifareTag tag, const MifareClassicBlockNumber block, int32_t *value, MifareClassicBlockNumber *adr); int mifare_classic_write (MifareTag tag, const MifareClassicBlockNumber block, const MifareClassicBlock data); int mifare_classic_increment (MifareTag tag, const MifareClassicBlockNumber block, const uint32_t amount); int mifare_classic_decrement (MifareTag tag, const MifareClassicBlockNumber block, const uint32_t amount); int mifare_classic_restore (MifareTag tag, const MifareClassicBlockNumber block); int mifare_classic_transfer (MifareTag tag, const MifareClassicBlockNumber block); int mifare_classic_get_trailer_block_permission (MifareTag tag, const MifareClassicBlockNumber block, const uint16_t permission, const MifareClassicKeyType key_type); int mifare_classic_get_data_block_permission (MifareTag tag, const MifareClassicBlockNumber block, const unsigned char permission, const MifareClassicKeyType key_type); int mifare_classic_format_sector (MifareTag tag, const MifareClassicSectorNumber sector); void mifare_classic_trailer_block (MifareClassicBlock *block, const MifareClassicKey key_a, uint8_t ab_0, uint8_t ab_1, uint8_t ab_2, uint8_t ab_tb, const uint8_t gpb, const MifareClassicKey key_b); MifareClassicSectorNumber mifare_classic_block_sector (MifareClassicBlockNumber block); MifareClassicBlockNumber mifare_classic_sector_first_block (MifareClassicSectorNumber sector); size_t mifare_classic_sector_block_count (MifareClassicSectorNumber sector); MifareClassicBlockNumber mifare_classic_sector_last_block (MifareClassicSectorNumber sector); #define C_000 0 #define C_001 1 #define C_010 2 #define C_011 3 #define C_100 4 #define C_101 5 #define C_110 6 #define C_111 7 #define C_DEFAULT 255 /* MIFARE Classic Access Bits */ #define MCAB_R 0x8 #define MCAB_W 0x4 #define MCAB_D 0x2 #define MCAB_I 0x1 #define MCAB_READ_KEYA 0x400 #define MCAB_WRITE_KEYA 0x100 #define MCAB_READ_ACCESS_BITS 0x040 #define MCAB_WRITE_ACCESS_BITS 0x010 #define MCAB_READ_KEYB 0x004 #define MCAB_WRITE_KEYB 0x001 struct mad_aid { uint8_t application_code; uint8_t function_cluster_code; }; typedef struct mad_aid MadAid; struct mad; typedef struct mad *Mad; /* MAD Public read key A */ extern const MifareClassicKey mad_public_key_a; /* AID - Adminisration codes */ extern const MadAid mad_free_aid; extern const MadAid mad_defect_aid; extern const MadAid mad_reserved_aid; extern const MadAid mad_card_holder_aid; extern const MadAid mad_not_applicable_aid; /* NFC Forum AID */ extern const MadAid mad_nfcforum_aid; Mad mad_new (const uint8_t version); Mad mad_read (MifareTag tag); int mad_write (MifareTag tag, Mad mad, const MifareClassicKey key_b_sector_00, const MifareClassicKey key_b_sector_10); int mad_get_version (Mad mad); void mad_set_version (Mad mad, const uint8_t version); MifareClassicSectorNumber mad_get_card_publisher_sector (Mad mad); int mad_set_card_publisher_sector (Mad mad, const MifareClassicSectorNumber cps); int mad_get_aid (Mad mad, const MifareClassicSectorNumber sector, MadAid *aid); int mad_set_aid (Mad mad, const MifareClassicSectorNumber sector, MadAid aid); bool mad_sector_reserved (const MifareClassicSectorNumber sector); void mad_free (Mad mad); MifareClassicSectorNumber *mifare_application_alloc (Mad mad, const MadAid aid, const size_t size); ssize_t mifare_application_read (MifareTag tag, Mad mad, const MadAid aid, void *buf, size_t nbytes, const MifareClassicKey key, const MifareClassicKeyType key_type); ssize_t mifare_application_write (MifareTag tag, Mad mad, const MadAid aid, const void *buf, size_t nbytes, const MifareClassicKey key, const MifareClassicKeyType key_type); void mifare_application_free (Mad mad, const MadAid aid); MifareClassicSectorNumber *mifare_application_find (Mad mad, const MadAid aid); /* File types */ enum mifare_desfire_file_types { MDFT_STANDARD_DATA_FILE = 0x00, MDFT_BACKUP_DATA_FILE = 0x01, MDFT_VALUE_FILE_WITH_BACKUP = 0x02, MDFT_LINEAR_RECORD_FILE_WITH_BACKUP = 0x03, MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP = 0x04 }; /* Communication mode */ #define MDCM_PLAIN 0x00 #define MDCM_MACED 0x01 #define MDCM_ENCIPHERED 0x03 /* Mifare DESFire EV1 Application crypto operations */ #define APPLICATION_CRYPTO_DES 0x00 #define APPLICATION_CRYPTO_3K3DES 0x40 #define APPLICATION_CRYPTO_AES 0x80 /* Access right */ #define MDAR(read,write,read_write,change_access_rights) ( \ (read << 12) | \ (write << 8) | \ (read_write << 4) | \ (change_access_rights) \ ) #define MDAR_READ(ar) (((ar) >> 12) & 0x0f) #define MDAR_WRITE(ar) (((ar) >> 8) & 0x0f) #define MDAR_READ_WRITE(ar) (((ar) >> 4) & 0x0f) #define MDAR_CHANGE_AR(ar) ((ar) & 0x0f) #define MDAR_KEY0 0x0 #define MDAR_KEY1 0x1 #define MDAR_KEY2 0x2 #define MDAR_KEY3 0x3 #define MDAR_KEY4 0x4 #define MDAR_KEY5 0x5 #define MDAR_KEY6 0x6 #define MDAR_KEY7 0x7 #define MDAR_KEY8 0x8 #define MDAR_KEY9 0x9 #define MDAR_KEY10 0xa #define MDAR_KEY11 0xb #define MDAR_KEY12 0xc #define MDAR_KEY13 0xd #define MDAR_FREE 0xE #define MDAR_DENY 0xF /* Status and error codes */ #define OPERATION_OK 0x00 #define NO_CHANGES 0x0C #define OUT_OF_EEPROM_ERROR 0x0E #define ILLEGAL_COMMAND_CODE 0x1C #define INTEGRITY_ERROR 0x1E #define NO_SUCH_KEY 0x40 #define LENGTH_ERROR 0x7E #define PERMISSION_ERROR 0x9D #define PARAMETER_ERROR 0x9E #define APPLICATION_NOT_FOUND 0xA0 #define APPL_INTEGRITY_ERROR 0xA1 #define AUTHENTICATION_ERROR 0xAE #define ADDITIONAL_FRAME 0xAF #define BOUNDARY_ERROR 0xBE #define PICC_INTEGRITY_ERROR 0xC1 #define COMMAND_ABORTED 0xCA #define PICC_DISABLED_ERROR 0xCD #define COUNT_ERROR 0xCE #define DUPLICATE_ERROR 0xDE #define EEPROM_ERROR 0xEE #define FILE_NOT_FOUND 0xF0 #define FILE_INTEGRITY_ERROR 0xF1 /* Error code managed by the library */ #define CRYPTO_ERROR 0x01 struct mifare_desfire_aid; typedef struct mifare_desfire_aid *MifareDESFireAID; struct mifare_desfire_df { uint32_t aid; uint16_t fid; uint8_t df_name[16]; size_t df_name_len; }; typedef struct mifare_desfire_df MifareDESFireDF; MifareDESFireAID mifare_desfire_aid_new (uint32_t aid); MifareDESFireAID mifare_desfire_aid_new_with_mad_aid (MadAid mad_aid, uint8_t n); uint32_t mifare_desfire_aid_get_aid (MifareDESFireAID aid); uint8_t mifare_desfire_last_pcd_error (MifareTag tag); uint8_t mifare_desfire_last_picc_error (MifareTag tag); #pragma pack (push) #pragma pack (1) struct mifare_desfire_version_info { struct { uint8_t vendor_id; uint8_t type; uint8_t subtype; uint8_t version_major; uint8_t version_minor; uint8_t storage_size; uint8_t protocol; } hardware; struct { uint8_t vendor_id; uint8_t type; uint8_t subtype; uint8_t version_major; uint8_t version_minor; uint8_t storage_size; uint8_t protocol; } software; uint8_t uid[7]; uint8_t batch_number[5]; uint8_t production_week; uint8_t production_year; }; #pragma pack (pop) struct mifare_desfire_file_settings { uint8_t file_type; uint8_t communication_settings; uint16_t access_rights; union { struct { uint32_t file_size; } standard_file; struct { int32_t lower_limit; int32_t upper_limit; int32_t limited_credit_value; uint8_t limited_credit_enabled; } value_file; struct { uint32_t record_size; uint32_t max_number_of_records; uint32_t current_number_of_records; } linear_record_file; } settings; }; int mifare_desfire_connect (MifareTag tag); int mifare_desfire_disconnect (MifareTag tag); int mifare_desfire_authenticate (MifareTag tag, uint8_t key_no, MifareDESFireKey key); int mifare_desfire_authenticate_iso (MifareTag tag, uint8_t key_no, MifareDESFireKey key); int mifare_desfire_authenticate_aes (MifareTag tag, uint8_t key_no, MifareDESFireKey key); int mifare_desfire_change_key_settings (MifareTag tag, uint8_t settings); int mifare_desfire_get_key_settings (MifareTag tag, uint8_t *settings, uint8_t *max_keys); int mifare_desfire_change_key (MifareTag tag, uint8_t key_no, MifareDESFireKey new_key, MifareDESFireKey old_key); int mifare_desfire_get_key_version (MifareTag tag, uint8_t key_no, uint8_t *version); int mifare_desfire_create_application (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no); int mifare_desfire_create_application_3k3des (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no); int mifare_desfire_create_application_aes (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no); int mifare_desfire_create_application_iso (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no, int want_iso_file_identifiers, uint16_t iso_file_id, uint8_t *iso_file_name, size_t iso_file_name_len); int mifare_desfire_create_application_3k3des_iso (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no, int want_iso_file_identifiers, uint16_t iso_file_id, uint8_t *iso_file_name, size_t iso_file_name_len); int mifare_desfire_create_application_aes_iso (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no, int want_iso_file_identifiers, uint16_t iso_file_id, uint8_t *iso_file_name, size_t iso_file_name_len); int mifare_desfire_delete_application (MifareTag tag, MifareDESFireAID aid); int mifare_desfire_get_application_ids (MifareTag tag, MifareDESFireAID *aids[], size_t *count); int mifare_desfire_get_df_names (MifareTag tag, MifareDESFireDF *dfs[], size_t *count); void mifare_desfire_free_application_ids (MifareDESFireAID aids[]); int mifare_desfire_select_application (MifareTag tag, MifareDESFireAID aid); int mifare_desfire_format_picc (MifareTag tag); int mifare_desfire_get_version (MifareTag tag, struct mifare_desfire_version_info *version_info); int mifare_desfire_free_mem (MifareTag tag, uint32_t *size); int mifare_desfire_set_configuration (MifareTag tag, bool disable_format, bool enable_random_uid); int mifare_desfire_set_default_key (MifareTag tag, MifareDESFireKey key); int mifare_desfire_set_ats (MifareTag tag, uint8_t *ats); int mifare_desfire_get_card_uid (MifareTag tag, char **uid); int mifare_desfire_get_file_ids (MifareTag tag, uint8_t *files[], size_t *count); int mifare_desfire_get_iso_file_ids (MifareTag tag, uint16_t *files[], size_t *count); int mifare_desfire_get_file_settings (MifareTag tag, uint8_t file_no, struct mifare_desfire_file_settings *settings); int mifare_desfire_change_file_settings (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights); int mifare_desfire_create_std_data_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size); int mifare_desfire_create_std_data_file_iso (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size, uint16_t iso_file_id); int mifare_desfire_create_backup_data_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size); int mifare_desfire_create_backup_data_file_iso (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size, uint16_t iso_file_id); int mifare_desfire_create_value_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, int32_t lower_limit, int32_t upper_limit, int32_t value, uint8_t limited_credit_enable); int mifare_desfire_create_linear_record_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records); int mifare_desfire_create_linear_record_file_iso (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records, uint16_t iso_file_id); int mifare_desfire_create_cyclic_record_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records); int mifare_desfire_create_cyclic_record_file_iso (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records, uint16_t iso_file_id); int mifare_desfire_delete_file (MifareTag tag, uint8_t file_no); ssize_t mifare_desfire_read_data (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data); ssize_t mifare_desfire_read_data_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs); ssize_t mifare_desfire_write_data (MifareTag tag, uint8_t file_no, off_t offset, size_t length, const void *data); ssize_t mifare_desfire_write_data_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, const void *data, int cs); int mifare_desfire_get_value (MifareTag tag, uint8_t file_no, int32_t *value); int mifare_desfire_get_value_ex (MifareTag tag, uint8_t file_no, int32_t *value, int cs); int mifare_desfire_credit (MifareTag tag, uint8_t file_no, int32_t amount); int mifare_desfire_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs); int mifare_desfire_debit (MifareTag tag, uint8_t file_no, int32_t amount); int mifare_desfire_debit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs); int mifare_desfire_limited_credit (MifareTag tag, uint8_t file_no, int32_t amount); int mifare_desfire_limited_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs); ssize_t mifare_desfire_write_record (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data); ssize_t mifare_desfire_write_record_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs); ssize_t mifare_desfire_read_records (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data); ssize_t mifare_desfire_read_records_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs); int mifare_desfire_clear_record_file (MifareTag tag, uint8_t file_no); int mifare_desfire_commit_transaction (MifareTag tag); int mifare_desfire_abort_transaction (MifareTag tag); MifareDESFireKey mifare_desfire_des_key_new (uint8_t value[8]); MifareDESFireKey mifare_desfire_3des_key_new (uint8_t value[16]); MifareDESFireKey mifare_desfire_des_key_new_with_version (uint8_t value[8]); MifareDESFireKey mifare_desfire_3des_key_new_with_version (uint8_t value[16]); MifareDESFireKey mifare_desfire_3k3des_key_new (uint8_t value[24]); MifareDESFireKey mifare_desfire_3k3des_key_new_with_version (uint8_t value[24]); MifareDESFireKey mifare_desfire_aes_key_new (uint8_t value[16]); MifareDESFireKey mifare_desfire_aes_key_new_with_version (uint8_t value[16], uint8_t version); uint8_t mifare_desfire_key_get_version (MifareDESFireKey key); void mifare_desfire_key_set_version (MifareDESFireKey key, uint8_t version); void mifare_desfire_key_free (MifareDESFireKey key); uint8_t *tlv_encode (const uint8_t type, const uint8_t *istream, uint16_t isize, size_t *osize); uint8_t *tlv_decode (const uint8_t *istream, uint8_t *type, uint16_t *size); size_t tlv_record_length (const uint8_t *istream, size_t *field_length_size, size_t *field_value_size); uint8_t *tlv_append (uint8_t *a, uint8_t *b); #ifdef __cplusplus } #endif // __cplusplus #endif /* !__FREEFARE_H__ */ libfreefare-0.4.0/libfreefare/freefare_error.3000066400000000000000000000100171225640673100213100ustar00rootroot00000000000000.\" Copyright (C) 2010 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id: mifare_desfire.3 495 2010-07-26 21:48:18Z rtartiere@il4p.fr $ .\" .Dd September 28, 2010 .Dt FREEFARE_ERROR 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm freefare_strerror , .Nm freefare_strerror_r , .Nm freefare_perror , .Nm mifare_desfire_last_picc_error .Nd Error Reporting Functions. .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft "const char *" .Fn freefare_strerror "MifareTag tag" .Ft "int" .Fn freefare_strerror_r "MifareTag tag" "char *buffer" "size_t len" .Ft "void" .Fn freefare_strerror "MifareTag tag" "char *string" .Ft "uint8_t" .Fn mifare_desfire_last_pcd_error "MifareTag tag" .Ft "uint8_t" .Fn mifare_desfire_last_picc_error "MifareTag tag" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION The .Fn freefare_strerror functions returns the error string corresponding to the last PCD or PICC error encounred using .Vt tag . .Pp The .Fn freefare_strerror_r functions does the same as .Fn freefare_strerror but the error message is copied into .Vt buffer for a maximum size of .Vt len chars. .Pp The .Fn freefare_strerror function displays the last PCD or PICC error encounred using .Vt tag to stderr. .Pp The .Fn mifare_desfire_last_pcd_error function returns the error code of the last function call from the library. .Pp The .Fn mifare_desfire_last_picc_error function returns the error code returned by the last command run on .Vt tag . .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES .Fn freefare_strerror returns the error message, .Fn freefare_strerror_r returns 0 on success, \-1 on failure; .Fn mifare_desfire_last_pcd_error and .Fn mifare_desfire_last_picc_error return an error code or 0 if no error occured or if .Vt tag is not a Mifare DESFire target. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr freefare 3 , .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@il4p.org libfreefare-0.4.0/libfreefare/freefare_internal.h000066400000000000000000000303221225640673100220610ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #ifndef __FREEFARE_INTERNAL_H__ #define __FREEFARE_INTERNAL_H__ #include "config.h" #include /* * Endienness macros * * POSIX does not describe any API for endianness problems, and solutions are * mostly vendor-dependant. Some operating systems provide a complete * framework for this (FreeBSD, OpenBSD), some provide nothing in the base * system (Mac OS), GNU/Linux systems may or may not provide macros to do the * conversion depending on the version of the libc. * * This is a PITA but unfortunately we have no other solution than doing all * this gymnastic. Depending of what is defined if one or more of endian.h, * sys/endian.h and byteswap.h was included, define a set of macros to stick to * the set of macros provided by FreeBSD (it's a historic choice: development * was done on this operating system when endianness problems first had to be * dealt with). */ #if !defined(le32toh) && defined(letoh32) # define le32toh(x) letoh32(x) # define be32toh(x) betoh32(x) #endif #if !defined(le16toh) && defined(letoh16) # define le16toh(x) letoh16(x) # define be16toh(x) betoh16(x) #endif #if !defined(le32toh) && defined(CFSwapInt32LittleToHost) # define be32toh(x) CFSwapInt32BigToHost(x) # define htobe32(x) CFSwapInt32HostToBig(x) # define le32toh(x) CFSwapInt32LittleToHost(x) # define htole32(x) CFSwapInt32HostToLittle(x) #endif #if !defined(le16toh) && defined(CFSwapInt16LittleToHost) # define be16toh(x) CFSwapInt16BigToHost(x) # define htobe16(x) CFSwapInt16HostToBig(x) # define le16toh(x) CFSwapInt16LittleToHost(x) # define htole16(x) CFSwapInt16HostToLittle(x) #endif #if !defined(le32toh) && defined(bswap_32) # if BYTE_ORDER == LITTLE_ENDIAN # define be32toh(x) bswap_32(x) # define htobe32(x) bswap_32(x) # define le32toh(x) (x) # define htole32(x) (x) # else # define be32toh(x) (x) # define htobe32(x) (x) # define le32toh(x) bswap_32(x) # define htole32(x) bswap_32(x) # endif #endif #if !defined(htole16) && defined(bswap_16) # if BYTE_ORDER == LITTLE_ENDIAN # define be16toh(x) (bswap_16(x)) # define htobe16(x) (bswap_16(x)) # define htole16(x) (x) # define le16toh(x) (x) # else # define be16toh(x) (x) # define htobe16(x) (x) # define htole16(x) (bswap_16(x)) # define le16toh(x) (bswap_16(x)) # endif #endif #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MAX_CRYPTO_BLOCK_SIZE 16 void *memdup (const void *p, const size_t n); struct mad_sector_0x00; struct mad_sector_0x10; void nxp_crc (uint8_t *crc, const uint8_t value); MifareTag mifare_classic_tag_new (void); void mifare_classic_tag_free (MifareTag tag); MifareTag mifare_desfire_tag_new (void); void mifare_desfire_tag_free (MifareTag tags); MifareTag mifare_ultralight_tag_new (void); void mifare_ultralight_tag_free (MifareTag tag); uint8_t sector_0x00_crc8 (Mad mad); uint8_t sector_0x10_crc8 (Mad mad); typedef enum { MCD_SEND, MCD_RECEIVE } MifareCryptoDirection; typedef enum { MCO_ENCYPHER, MCO_DECYPHER } MifareCryptoOperation; #define MDCM_MASK 0x000F #define CMAC_NONE 0 // Data send to the PICC is used to update the CMAC #define CMAC_COMMAND 0x010 // Data received from the PICC is used to update the CMAC #define CMAC_VERIFY 0x020 // MAC the command (when MDCM_MACED) #define MAC_COMMAND 0x100 // The command returns a MAC to verify (when MDCM_MACED) #define MAC_VERIFY 0x200 #define ENC_COMMAND 0x1000 #define NO_CRC 0x2000 #define MAC_MASK 0x0F0 #define CMAC_MACK 0xF00 void *mifare_cryto_preprocess_data (MifareTag tag, void *data, size_t *nbytes, off_t offset, int communication_settings); void *mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int communication_settings); void mifare_cypher_single_block (MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size); void mifare_cypher_blocks_chained (MifareTag tag, MifareDESFireKey key, uint8_t *ivect, uint8_t *data, size_t data_size, MifareCryptoDirection direction, MifareCryptoOperation operation); void rol (uint8_t *data, const size_t len); void desfire_crc32 (const uint8_t *data, const size_t len, uint8_t *crc); void desfire_crc32_append (uint8_t *data, const size_t len); size_t key_block_size (const MifareDESFireKey key); size_t padded_data_length (const size_t nbytes, const size_t block_size); size_t maced_data_length (const MifareDESFireKey key, const size_t nbytes); size_t enciphered_data_length (const MifareTag tag, const size_t nbytes, int communication_settings); void cmac_generate_subkeys (MifareDESFireKey key); void cmac (const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac); void *assert_crypto_buffer_size (MifareTag tag, size_t nbytes); #define MIFARE_ULTRALIGHT_PAGE_COUNT 0x10 #define MIFARE_ULTRALIGHT_C_PAGE_COUNT 0x30 #define MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ 0x2C // Max PAGE_COUNT of the Ultralight Family: #define MIFARE_ULTRALIGHT_MAX_PAGE_COUNT 0x30 struct supported_tag { enum mifare_tag_type type; const char *friendly_name; uint8_t SAK; uint8_t ATS_min_length; uint8_t ATS_compare_length; uint8_t ATS[5]; bool (*check_tag_on_reader) (nfc_device *, nfc_iso14443a_info); }; /* * This structure is common to all supported MIFARE targets but shall not be * used directly (it's some kind of abstract class). All members in this * structure are initialized by freefare_get_tags(). * * Extra members in derived classes are initialized in the correpsonding * mifare_*_connect() function. */ struct mifare_tag { nfc_device *device; nfc_iso14443a_info info; const struct supported_tag *tag_info; int active; }; struct mifare_classic_tag { struct mifare_tag __tag; MifareClassicKeyType last_authentication_key_type; /* * The following block numbers are on 2 bytes in order to use invalid * address and avoid false cache hit with inconsistent data. */ struct { int16_t sector_trailer_block_number; uint16_t sector_access_bits; int16_t block_number; uint8_t block_access_bits; } cached_access_bits; }; struct mifare_desfire_aid { uint8_t data[3]; }; struct mifare_desfire_key { uint8_t data[24]; enum { T_DES, T_3DES, T_3K3DES, T_AES } type; DES_key_schedule ks1; DES_key_schedule ks2; DES_key_schedule ks3; uint8_t cmac_sk1[24]; uint8_t cmac_sk2[24]; uint8_t aes_version; }; struct mifare_desfire_tag { struct mifare_tag __tag; uint8_t last_picc_error; uint8_t last_internal_error; uint8_t last_pcd_error; MifareDESFireKey session_key; enum { AS_LEGACY, AS_NEW } authentication_scheme; uint8_t authenticated_key_no; uint8_t ivect[MAX_CRYPTO_BLOCK_SIZE]; uint8_t cmac[16]; uint8_t *crypto_buffer; size_t crypto_buffer_size; uint32_t selected_application; }; MifareDESFireKey mifare_desfire_session_key_new (uint8_t rnda[8], uint8_t rndb[8], MifareDESFireKey authentication_key); const char *mifare_desfire_error_lookup (uint8_t error); struct mifare_ultralight_tag { struct mifare_tag __tag; /* mifare_ultralight_read() reads 4 pages at a time (wrapping) */ MifareUltralightPage cache[MIFARE_ULTRALIGHT_MAX_PAGE_COUNT + 3]; uint8_t cached_pages[MIFARE_ULTRALIGHT_MAX_PAGE_COUNT]; }; /* * MifareTag assertion macros * * This macros provide a simple and unified way to perform various tests at the * beginning of the different targets functions. */ #define ASSERT_ACTIVE(tag) do { if (!tag->active) return errno = ENXIO, -1; } while (0) #define ASSERT_INACTIVE(tag) do { if (tag->active) return errno = ENXIO, -1; } while (0) #define ASSERT_MIFARE_CLASSIC(tag) do { if ((tag->tag_info->type != CLASSIC_1K) && (tag->tag_info->type != CLASSIC_4K)) return errno = ENODEV, -1; } while (0) #define ASSERT_MIFARE_DESFIRE(tag) do { if (tag->tag_info->type != DESFIRE) return errno = ENODEV, -1; } while (0) #define IS_MIFARE_ULTRALIGHT_C(tag) (tag->tag_info->type == ULTRALIGHT_C) #define ASSERT_MIFARE_ULTRALIGHT(tag) do { if ((tag->tag_info->type != ULTRALIGHT) && (! IS_MIFARE_ULTRALIGHT_C(tag))) return errno = ENODEV, -1; } while (0) #define ASSERT_MIFARE_ULTRALIGHT_C(tag) do { if (! IS_MIFARE_ULTRALIGHT_C(tag)) return errno = ENODEV, -1; } while (0) /* * MifareTag cast macros * * This macros are intended to provide a convenient way to cast abstract * MifareTag structures to concrete Tags (e.g. MIFARE Classic tag). */ #define MIFARE_CLASSIC(tag) ((struct mifare_classic_tag *) tag) #define MIFARE_DESFIRE(tag) ((struct mifare_desfire_tag *) tag) #define MIFARE_ULTRALIGHT(tag) ((struct mifare_ultralight_tag *) tag) /* * Access bits manipulation macros */ #define DB_AB(ab) ((ab == C_DEFAULT) ? C_000 : ab) #define TB_AB(ab) ((ab == C_DEFAULT) ? C_100 : ab) #ifdef WITH_DEBUG #define DEBUG_XFER(data, nbytes, hint) do { hexdump (data, nbytes, hint, 0); } while (0) #else #define DEBUG_XFER(data, nbytes, hint) do {} while (0) #endif /* * Buffer management macros. * * The following macros ease setting-up and using buffers: * BUFFER_INIT (data, 5); // data -> [ xx, xx, xx, xx, xx ] * BUFFER_SIZE (data); // size -> 0 * BUFFER_APPEND (data, 0x12); // data -> [ 12, xx, xx, xx, xx ] * BUFFER_SIZE (data); // size -> 1 * uint16_t x = 0x3456; // We suppose we are little endian * BUFFER_APPEND_BYTES (data, x, 2); * // data -> [ 12, 56, 34, xx, xx ] * BUFFER_SIZE (data); // size -> 3 * BUFFER_APPEND_LE (data, x, 2, sizeof (x)); * // data -> [ 12, 56, 34, 34, 56 ] * BUFFER_SIZE (data); // size -> 5 */ /* * Initialise a buffer named buffer_name of size bytes. */ #define BUFFER_INIT(buffer_name, size) \ uint8_t buffer_name[size]; \ size_t __##buffer_name##_size = size; \ size_t __##buffer_name##_n = 0 /* * Create a wrapper for an existing buffer. * BEWARE! It eats children! */ #define BUFFER_ALIAS(buffer_name, origin, origin_size) \ uint8_t *buffer_name = (void *)origin; \ size_t __##buffer_name##_size = origin_size; \ size_t __##buffer_name##_n = 0; #define BUFFER_SIZE(buffer_name) (__##buffer_name##_n) #define BUFFER_CLEAR(buffer_name) (__##buffer_name##_n = 0) /* * Append one byte of data to the buffer buffer_name. */ #define BUFFER_APPEND(buffer_name, data) \ do { \ buffer_name[__##buffer_name##_n++] = data; \ } while (0) /* * Append size bytes of data to the buffer buffer_name. */ #define BUFFER_APPEND_BYTES(buffer_name, data, size) \ do { \ size_t __n = 0; \ while (__n < size) { \ buffer_name[__##buffer_name##_n++] = ((uint8_t *)data)[__n++]; \ } \ } while (0) /* * Append data_size bytes of data at the end of the buffer. Since data is * copied as a little endian value, the storage size of the value has to be * passed as the field_size parameter. * * Example: to copy 24 bits of data from a 32 bits value: * BUFFER_APPEND_LE (buffer, data, 3, 4); */ #if defined(_BYTE_ORDER) && (_BYTE_ORDER != _LITTLE_ENDIAN) #define BUFFER_APPEND_LE(buffer, data, data_size, field_size) \ do { \ size_t __data_size = data_size; \ size_t __field_size = field_size; \ while (__field_size--, __data_size--) { \ buffer[__##buffer##_n++] = ((uint8_t *)&data)[__field_size]; \ } \ } while (0) #else #define BUFFER_APPEND_LE(buffer, data, data_size, field_size) \ do { \ memcpy (buffer + __##buffer##_n, &data, data_size); \ __##buffer##_n += data_size; \ } while (0) #endif #endif /* !__FREEFARE_INTERNAL_H__ */ libfreefare-0.4.0/libfreefare/mad.3000066400000000000000000000127621225640673100170720ustar00rootroot00000000000000.\" Copyright (C) 2010 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id$ .\" .Dd March 30, 2010 .Dt MAD 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm mad_new , .Nm mad_read , .Nm mad_write , .Nm mad_get_version , .Nm mad_set_version , .Nm mad_get_card_publisher_sector , .Nm mad_set_card_publisher_sector , .Nm mad_get_aid , .Nm mad_set_aid , .Nm mad_free , .Nd "Mifare Application Directory (MAD) Manipulation Functions" .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft "Mad" .Fn mad_new "uint8_t version" .Ft "Mad" .Fn mad_read "MifareTag tag" .Ft "int" .Fn mad_write "MifareTag tag" "Mad mad" "MifareClassicKey key_b_sector_00" "MifareClassicKey key_b_sector_10" .Ft "int" .Fn mad_get_version "Mad mad" .Ft "void" .Fn mad_set_version "Mad mad" "uint8_t version" .Ft "MifareClassicSectorNumber" .Fn mad_get_card_publisher_sector "Mad mad" .Ft "int" .Fn mad_set_card_publisher_sector "Mad mad" "MifareClassicSectorNumber cps" .Ft "int" .Fn mad_get_aid "Mad mad" "MifareClassicSectorNumber sector" "MadAid *aid" .Ft "int" .Fn mad_set_aid "Mad mad" "MifareClassicSectorNumber sector" "MadAid aid" .Ft "void" .Fn mad_free "Mad mad" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION Mifare Application Directories (MAD) can be easily manipulated using the .Fn mad_* set of functions. .Pp A .Vt mad can be loaded form a Mifare Classic .Vt tag using .Fn mad_read or generated from scratch using .Fn mad_new and providing the .Vt version of the .Vt mad to generate. After using a .Vt mad , the memory can be reclaimed using .Fn mad_free . .Pp A .Vt mad can be written to a Mifare Classic .Vt tag using .Fn mad_write and providing the B keys required for writing to sectors 0, .Vt key_b_sector_00, and the one required for writing to sectors 10, .Vt key_b_sector_10 . When writing a MAD version 1 .Vt mad , the .Vt key_b_sector_10 . .Pp The .Vt version of a MAD .Vt mad can be read using .Fn mad_get_version and changed using .Fn mad_set_version . .Pp The card publisher sector number .Vt cps can be read read using .Fn mad_get_card_publisher_sector and writen using .Fn mad_set_card_publisher_sector . .Pp Sectors allocation in the .Vt mad is handled by the .Fn mad_get_aid and .Fn mad_set_aid functions. These functions fill-in or read the Application Identifier, .Vt aid for the given .Vt sector . .\" ___ _ _ _ _ _ .\" |_ _|_ __ ___ _ __ | | ___ _ __ ___ ___ _ __ | |_ __ _| |_(_) ___ _ __ _ __ ___ | |_ ___ ___ .\" | || '_ ` _ \| '_ \| |/ _ \ '_ ` _ \ / _ \ '_ \| __/ _` | __| |/ _ \| '_ \ | '_ \ / _ \| __/ _ \/ __| .\" | || | | | | | |_) | | __/ | | | | | __/ | | | || (_| | |_| | (_) | | | | | | | | (_) | || __/\__ \ .\" |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_| |_| |_|\___/ \__\___||___/ .\" |_| .\".Sh IMPLEMENTATION NOTES .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES The .Fn mad_new function return .Va NULL on failure and allocates memory that has to be freed using .Fn mad_free on success. Unless stated otherwise, all other functions return a value greater than or equal to .Va 0 on success or .Va -1 on failure. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr freefare 3 , .Xr mifare_application 3 , .Xr mifare_classic 3 .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@blogreen.org .An Romuald Conty Aq romuald@libnfc.org libfreefare-0.4.0/libfreefare/mad.c000066400000000000000000000261461225640673100171530ustar00rootroot00000000000000/*- * Copyright (C) 2009, 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ /* * This implementation was written based on information provided by the * following document: * * AN10787 * MIFARE Application Directory (MAD) * Rev. 04 - 5 March 2009 * * NXP Type MF1K/4K Tag Operation * Storing NFC Forum data in Mifare Standard 1k/4k * Rev. 1.1 - 21 August 2007 */ #include "config.h" #include #include #include #include #include #include /* * The documentation says the preset is 0xE3 but the bits have to be mirrored: * 0xe3 = 1110 0011 <=> 1100 0111 = 0xc7 */ #define CRC_PRESET 0xc7 #define SECTOR_0X00_AIDS 15 #define SECTOR_0X10_AIDS 23 struct mad_sector_0x00 { uint8_t crc; uint8_t info; MadAid aids[SECTOR_0X00_AIDS]; }; struct mad_sector_0x10 { uint8_t crc; uint8_t info; MadAid aids[SECTOR_0X10_AIDS]; }; struct mad { struct mad_sector_0x00 sector_0x00; struct mad_sector_0x10 sector_0x10; uint8_t version; }; /* Public Key A value of MAD sector(s) */ const MifareClassicKey mad_public_key_a = { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5 }; /* AID - Administration codes: */ /* if sector is free */ const MadAid mad_free_aid = { .function_cluster_code = 0x00, .application_code = 0x00, }; /* if sector is defect, e.g. access keys are destroyed or unknown */ const MadAid mad_defect_aid = { .function_cluster_code = 0x00, .application_code = 0x01, }; /* if sector is reserved */ const MadAid mad_reserved_aid = { .function_cluster_code = 0x00, .application_code = 0x02, }; /* if sector contains card holder information in ASCII format. */ const MadAid mad_card_holder_aid = { .function_cluster_code = 0x00, .application_code = 0x04, }; /* if sector not applicable (above memory size) */ const MadAid mad_not_applicable_aid = { .function_cluster_code = 0x00, .application_code = 0x05, }; /* NFC Forum AID */ const MadAid mad_nfcforum_aid = { .function_cluster_code = 0xe1, .application_code = 0x03, }; /* * Allocate an empty new MAD. */ Mad mad_new (uint8_t version) { Mad mad = malloc (sizeof (*mad)); if (!mad) return NULL; mad->version = version; memset (&(mad->sector_0x00), 0, sizeof (mad->sector_0x00)); memset (&(mad->sector_0x10), 0, sizeof (mad->sector_0x10)); return mad; } /* * Compute CRC. */ void nxp_crc (uint8_t *crc, const uint8_t value) { /* x^8 + x^4 + x^3 + x^2 + 1 => 0x11d */ const uint8_t poly = 0x1d; *crc ^= value; for (int current_bit = 7; current_bit >= 0; current_bit--) { int bit_out = (*crc) & 0x80; *crc <<= 1; if (bit_out) *crc ^= poly; } } uint8_t sector_0x00_crc8 (Mad mad) { uint8_t crc = CRC_PRESET; nxp_crc (&crc, mad->sector_0x00.info); for (int n = 0; n < SECTOR_0X00_AIDS; n++) { nxp_crc (&crc, mad->sector_0x00.aids[n].application_code); nxp_crc (&crc, mad->sector_0x00.aids[n].function_cluster_code); } return crc; } uint8_t sector_0x10_crc8 (Mad mad) { uint8_t crc = CRC_PRESET; nxp_crc (&crc, mad->sector_0x10.info); for (int n = 0; n < SECTOR_0X10_AIDS; n++) { nxp_crc (&crc, mad->sector_0x10.aids[n].application_code); nxp_crc (&crc, mad->sector_0x10.aids[n].function_cluster_code); } return crc; } /* * Read a MAD from the provided MIFARE tag. */ Mad mad_read (MifareTag tag) { Mad mad = malloc (sizeof (*mad)); if (!mad) goto error; /* Authenticate using MAD key A */ if (mifare_classic_authenticate (tag, 0x03, mad_public_key_a, MFC_KEY_A) < 0) { goto error; } /* Read first sector trailer block */ MifareClassicBlock data; if (mifare_classic_read (tag, 0x03, &data) < 0) { goto error; } uint8_t gpb = data[9]; /* Check MAD availability (DA bit) */ if (!(gpb & 0x80)) { goto error; } /* Get MAD version (ADV bits) */ switch (gpb & 0x03) { case 0x01: mad->version = 1; break; case 0x02: mad->version = 2; break; default: /* MAD enabled but version not supported */ errno = ENOTSUP; goto error; } /* Read MAD data at 0x00 (MAD1, MAD2) */ if (mifare_classic_read (tag, 0x01, &data) < 0) goto error; uint8_t *p = (uint8_t *) &(mad->sector_0x00); memcpy (p, data, sizeof (data)); p+= sizeof (data); if (mifare_classic_read (tag, 0x02, &data) < 0) goto error; memcpy (p, data, sizeof (data)); uint8_t crc = mad->sector_0x00.crc; uint8_t computed_crc = sector_0x00_crc8 (mad); if (crc != computed_crc) goto error; /* Read MAD data at 0x10 (MAD2) */ if (mad->version == 2) { /* Authenticate using MAD key A */ if (mifare_classic_authenticate (tag, 0x43, mad_public_key_a, MFC_KEY_A) < 0) { goto error; } p = (uint8_t *) &(mad->sector_0x10); if (mifare_classic_read (tag, 0x40, &data) < 0) goto error; memcpy (p, data, sizeof (data)); p += sizeof (data); if (mifare_classic_read (tag, 0x41, &data) < 0) goto error; memcpy (p, data, sizeof (data)); p += sizeof (data); if (mifare_classic_read (tag, 0x42, &data) < 0) goto error; memcpy (p, data, sizeof (data)); crc = mad->sector_0x10.crc; computed_crc = sector_0x10_crc8 (mad); if (crc != computed_crc) goto error; } return mad; error: free (mad); return NULL; } /* * Write the mad to the provided MIFARE tad using the provided Key-B keys. */ int mad_write (MifareTag tag, Mad mad, const MifareClassicKey key_b_sector_00, const MifareClassicKey key_b_sector_10) { MifareClassicBlock data; if (mifare_classic_authenticate (tag, 0x00, key_b_sector_00, MFC_KEY_B) < 0) return -1; if ((1 != mifare_classic_get_data_block_permission (tag, 0x01, MCAB_W, MFC_KEY_B)) || (1 != mifare_classic_get_data_block_permission (tag, 0x02, MCAB_W, MFC_KEY_B)) || (1 != mifare_classic_get_trailer_block_permission (tag, 0x03, MCAB_WRITE_KEYA, MFC_KEY_B)) || (1 != mifare_classic_get_trailer_block_permission (tag, 0x03, MCAB_WRITE_ACCESS_BITS, MFC_KEY_B))) { errno = EPERM; return -1; } uint8_t gpb = 0x80; /* * FIXME Handle mono-application cards */ gpb |= 0x40; /* Write MAD version */ switch (mad->version) { case 1: gpb |= 0x01; break; case 2: gpb |= 0x02; break; } if (2 == mad->version) { if (mifare_classic_authenticate (tag, 0x40, key_b_sector_10, MFC_KEY_B) < 0) return -1; if ((1 != mifare_classic_get_data_block_permission (tag, 0x40, MCAB_W, MFC_KEY_B)) || (1 != mifare_classic_get_data_block_permission (tag, 0x41, MCAB_W, MFC_KEY_B)) || (1 != mifare_classic_get_data_block_permission (tag, 0x42, MCAB_W, MFC_KEY_B)) || (1 != mifare_classic_get_trailer_block_permission (tag, 0x43, MCAB_WRITE_KEYA, MFC_KEY_B)) || (1 != mifare_classic_get_trailer_block_permission (tag, 0x43, MCAB_WRITE_ACCESS_BITS, MFC_KEY_B))) { errno = EPERM; return -1; } mad->sector_0x10.crc = sector_0x10_crc8 (mad); memcpy (data, (uint8_t *)&(mad->sector_0x10), sizeof (data)); if (mifare_classic_write (tag, 0x40, data) < 0) return -1; memcpy (data, (uint8_t *)&(mad->sector_0x10) + sizeof (data), sizeof (data)); if (mifare_classic_write (tag, 0x41, data) < 0) return -1; memcpy (data, (uint8_t *)&(mad->sector_0x10) + sizeof (data) * 2, sizeof (data)); if (mifare_classic_write (tag, 0x42, data) < 0) return -1; mifare_classic_trailer_block (&data, mad_public_key_a, 0x0, 0x1, 0x1, 0x6, 0x00, key_b_sector_10); if (mifare_classic_write (tag, 0x43, data) < 0) return -1; } mad->sector_0x00.crc = sector_0x00_crc8 (mad); if (mifare_classic_authenticate (tag, 0x00, key_b_sector_00, MFC_KEY_B) < 0) return -1; memcpy (data, (uint8_t *)&(mad->sector_0x00), sizeof (data)); if (mifare_classic_write (tag, 0x01, data) < 0) return -1; memcpy (data, (uint8_t *)&(mad->sector_0x00) + sizeof (data), sizeof (data)); if (mifare_classic_write (tag, 0x02, data) < 0) return -1; mifare_classic_trailer_block (&data, mad_public_key_a, 0x0, 0x1, 0x1, 0x6, gpb, key_b_sector_00); if (mifare_classic_write (tag, 0x03, data) < 0) return -1; return 0; } /* * Return a MAD version. */ int mad_get_version (Mad mad) { return mad->version; } /* * Set a MAD version. */ void mad_set_version (Mad mad, const uint8_t version) { if ((version == 2) && (mad->version == 1)) { /* We use a larger MAD so initialise the new blocks */ memset (&(mad->sector_0x10), 0, sizeof (mad->sector_0x10)); } mad->version = version; } /* * Return the MAD card publisher sector. */ MifareClassicSectorNumber mad_get_card_publisher_sector(Mad mad) { return (mad->sector_0x00.info & 0x3f); } /* * Set the MAD card publisher sector. */ int mad_set_card_publisher_sector(Mad mad, const MifareClassicSectorNumber cps) { if (((mad->version == 2) && (cps > 0x27)) | (mad->version == 1) && (cps > 0x0f)) { errno = EINVAL; return -1; } mad->sector_0x00.info = (cps & 0x3f); return 0; } /* * Get the provided sector's application identifier. */ int mad_get_aid(Mad mad, const MifareClassicSectorNumber sector, MadAid *aid) { if ((sector < 1) || (sector == 0x10) || (sector > 0x27)) { errno = EINVAL; return -1; } if (sector > 0x0f) { if (mad->version != 2) { errno = EINVAL; return -1; } aid->function_cluster_code = mad->sector_0x10.aids[sector - 0x0f - 2].function_cluster_code; aid->application_code = mad->sector_0x10.aids[sector - 0x0f - 2].application_code; } else { aid->function_cluster_code = mad->sector_0x00.aids[sector - 1].function_cluster_code; aid->application_code = mad->sector_0x00.aids[sector - 1].application_code; } return 0; } /* * Set the provided sector's application identifier. */ int mad_set_aid(Mad mad, const MifareClassicSectorNumber sector, MadAid aid) { if ((sector < 1) || (sector == 0x10) || (sector > 0x27)) { errno = EINVAL; return -1; } if (sector > 0x0f) { if (mad->version != 2) { errno = EINVAL; return -1; } mad->sector_0x10.aids[sector - 0x0f - 2].function_cluster_code = aid.function_cluster_code; mad->sector_0x10.aids[sector - 0x0f - 2].application_code = aid.application_code; } else { mad->sector_0x00.aids[sector - 1].function_cluster_code = aid.function_cluster_code; mad->sector_0x00.aids[sector - 1].application_code = aid.application_code; } return 0; } bool mad_sector_reserved (const MifareClassicSectorNumber sector) { return ((0x00 == sector) || (0x10 == sector)); } /* * Free memory allocated by mad_new() and mad_read(). */ void mad_free (Mad mad) { free (mad); } libfreefare-0.4.0/libfreefare/mifare_application.3000066400000000000000000000127161225640673100221560ustar00rootroot00000000000000.\" Copyright (C) 2010 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id$ .\" .Dd March 30, 2010 .Dt MIFARE_APPLICATION 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm mifare_application_alloc , .Nm mifare_application_find , .Nm mifare_application_free , .Nm mifare_application_read , .Nm mifare_application_write .Nd Mifare Applications Manipulation Functions .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft "MifareClassicSectorNumber *" .Fn mifare_application_alloc "Mad mad" "MadAid aid" "size_t size" .Ft "void" .Fn mifare_application_free "Mad mad" "MadAid aid" .Ft "MifareClassicSectorNumber *" .Fn mifare_application_find "Mad mad" "MadAid aid" .Ft "ssize_t" .Fn mifare_application_read "MifareTag tag" "Mad mad" "MadAid aid" "void *buf" "size_t nbytes" "MifareClassicKey key" "MifareClassicKeyType key_type" .Ft "ssize_t" .Fn mifare_application_write "MifareTag tag" "Mad mad" "MadAid aid" "const void *buf" "size_t nbytes" "MifareClassicKey key" "MifareClassicKeyType key_type" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION The .Fn mifare_application_* functions facilitate management of applications in a Mifare Application Directory. .Pp The .Fn mifare_application_alloc function allocates enought sectors to store .Vt size bytes for the Application Identifier .Vt aid and returns the list of allocated sectors. .Pp The list of the sectors previously allocated for .Vt aid in a .Vt mad can be requested using .Fn mifare_application_find . .Pp An application can be removed from a .Vt mad using .Fn mifare_application_free . .Pp The .Fn mifare_application_read reads at most .Vt nbytes of the application identified by .Vt aid in the .Vt mad on the .Vt tag and copy them into .Vt buf. The function returns the amount of data it copied, or \-1 on error. .Pp The .Fn mifare_application_write functions writes at most .Vt nbytes of .Vt buf in the application identified by .Vt aid on the .Vt mad of the .Vt tag and returns the quantity of data written, or \-1 on error. .\" ___ _ _ _ _ _ .\" |_ _|_ __ ___ _ __ | | ___ _ __ ___ ___ _ __ | |_ __ _| |_(_) ___ _ __ _ __ ___ | |_ ___ ___ .\" | || '_ ` _ \| '_ \| |/ _ \ '_ ` _ \ / _ \ '_ \| __/ _` | __| |/ _ \| '_ \ | '_ \ / _ \| __/ _ \/ __| .\" | || | | | | | |_) | | __/ | | | | | __/ | | | || (_| | |_| | (_) | | | | | | | | (_) | || __/\__ \ .\" |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_| |_| |_|\___/ \__\___||___/ .\" |_| .Sh IMPLEMENTATION NOTES The .Fn mifare_application_alloc function will try to avoid wasting space and might not allocate sectors sequentially if a large amount of space is requested and the target has sectors of different size. .Pp The .Vt nbytes argument of .Fn mifare_application_read and .Fn mifare_application_write does not need to be aligned on blocks not sectors. .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES Unless stated otherwise, all functions return a value greater than or equal to .Va 0 on success or .Va -1 on failure. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr mad 3 , .Xr mifare_classic 3 .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@blogreen.org .An Romuald Conty Aq romuald@libnfc.org libfreefare-0.4.0/libfreefare/mifare_application.c000066400000000000000000000155571225640673100222440ustar00rootroot00000000000000/*- * Copyright (C) 2009, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ /* * This implementation was written based on information provided by the * following document: * * /dev/brain */ #include "config.h" #include #include #include #include #include "freefare_internal.h" #define FIRST_SECTOR 1 int aidcmp (const MadAid left, const MadAid right); size_t count_aids (const Mad mad, const MadAid aid); /* * Get the number of sectors allocated in the MAD for the provided application. */ size_t count_aids (const Mad mad, const MadAid aid) { size_t result = 0; MifareClassicSectorNumber s_max = (mad_get_version (mad) == 1) ? 0x0f : 0x27; /* Count application sectors */ MadAid c_aid; for (MifareClassicSectorNumber s = FIRST_SECTOR; s <= s_max; s++) { mad_get_aid (mad, s, &c_aid); if (0 == aidcmp (aid, c_aid)) { result++; } } return result; } /* * Compare two application identifiers. */ inline int aidcmp (const MadAid left, const MadAid right) { return ((left.function_cluster_code - right.function_cluster_code) << 8) | (left.application_code - right.application_code); } /* * Card publisher functions (MAD owner). */ /* * Allocates a new application into a MAD. */ MifareClassicSectorNumber * mifare_application_alloc (Mad mad, MadAid aid, size_t size) { uint8_t sector_map[40]; MifareClassicSectorNumber sector; MadAid sector_aid; MifareClassicSectorNumber *res = NULL; ssize_t s = size; /* * Ensure the card does not already have the application registered. */ MifareClassicSectorNumber *found; if ((found = mifare_application_find (mad, aid))) { free (found); return NULL; } for (size_t i = 0; i < sizeof (sector_map); i++) sector_map[i] = 0; /* * Try to minimize lost space and allocate as many large pages as possible * when the target is a Mifare Classic 4k. */ MadAid free_aid = { 0x00, 0x00 }; if (mad_get_version (mad) == 2) { sector = 32; while ((s >= 12*16) && sector < 40) { mad_get_aid (mad, sector, §or_aid); if (0 == aidcmp (sector_aid, free_aid)) { sector_map[sector] = 1; s -= 15*16; } sector++; } } sector = FIRST_SECTOR; MifareClassicSectorNumber s_max = (mad_get_version (mad) == 1) ? 15 : 31; while ((s > 0) && (sector <= s_max)) { if (mad_sector_reserved (sector)) continue; mad_get_aid (mad, sector, §or_aid); if (0 == aidcmp (sector_aid, free_aid)) { sector_map[sector] = 1; s -= 3*16; } sector++; } /* * Ensure the remaining free space is suficient before destroying the MAD. */ if (s > 0) return NULL; int n = 0; for (size_t i = FIRST_SECTOR; i < sizeof (sector_map); i++) if (sector_map[i]) n++; if (!(res = malloc (sizeof (*res) * (n+1)))) return NULL; n = 0; for (size_t i = FIRST_SECTOR; i < sizeof (sector_map); i++) if (sector_map[i]) { res[n] = i; mad_set_aid (mad, i, aid); n++; } res[n] = 0; /* Return the list of allocated sectors */ return res; } /* * Remove an application from a MAD. */ void mifare_application_free (Mad mad, MadAid aid) { MifareClassicSectorNumber *sectors = mifare_application_find (mad, aid); MifareClassicSectorNumber *p = sectors; MadAid free_aid = { 0x00, 0x00 }; while (*p) { mad_set_aid (mad, *p, free_aid); p++; } free (sectors); } /* * Application owner functions. */ /* * Get all sector numbers of an application from the provided MAD. */ MifareClassicSectorNumber * mifare_application_find (Mad mad, MadAid aid) { MifareClassicSectorNumber *res = NULL; size_t res_count = count_aids (mad, aid); if (res_count) res = malloc (sizeof (*res) * (res_count + 1)); size_t r = FIRST_SECTOR, w = 0; if (res) { /* Fill in the result */ MadAid c_aid; while (w < res_count) { mad_get_aid (mad, r, &c_aid); if (0 == aidcmp (c_aid, aid)) { res[w++] = r; } r++; } res[w] = 0; } return res; } ssize_t mifare_application_read (MifareTag tag, Mad mad, const MadAid aid, void *buf, size_t nbytes, const MifareClassicKey key, const MifareClassicKeyType key_type) { ssize_t res = 0; MifareClassicSectorNumber *sectors = mifare_application_find (mad, aid); MifareClassicSectorNumber *s = sectors; if (!sectors) return errno = EBADF, -1; while (*s && nbytes && (res >= 0)) { MifareClassicBlockNumber first_block = mifare_classic_sector_first_block (*s); MifareClassicBlockNumber last_block = mifare_classic_sector_last_block (*s); MifareClassicBlockNumber b = first_block; MifareClassicBlock block; if (mifare_classic_authenticate (tag, first_block, key, key_type) < 0) { res = -1; break; } while ((b < last_block) && nbytes) { size_t n = MIN (nbytes, 16); if (mifare_classic_read (tag, b, &block) < 0) { res = -1; break; } memcpy ((uint8_t *)buf + res, &block, n); nbytes -= n; res += n; b++; } s++; } free (sectors); return res; } ssize_t mifare_application_write (MifareTag tag, Mad mad, const MadAid aid, const void *buf, size_t nbytes, const MifareClassicKey key, const MifareClassicKeyType key_type) { ssize_t res = 0; MifareClassicSectorNumber *sectors = mifare_application_find (mad, aid); MifareClassicSectorNumber *s = sectors; if (!sectors) return errno = EBADF, -1; while (*s && nbytes && (res >= 0)) { MifareClassicBlockNumber first_block = mifare_classic_sector_first_block (*s); MifareClassicBlockNumber last_block = mifare_classic_sector_last_block (*s); MifareClassicBlockNumber b = first_block; MifareClassicBlock block; if (mifare_classic_authenticate (tag, first_block, key, key_type) < 0) { res = -1; break; } while ((b < last_block) && nbytes) { size_t n = MIN (nbytes, 16); // Avoid overwriting existing data with uninitialized memory. if (n < 16) { if (mifare_classic_read (tag, b, &block) < 0) { res = -1; break; } } memcpy (&block, (uint8_t *)buf + res, n); if (mifare_classic_write (tag, b, block) < 0) { res = -1; break; } nbytes -= n; res += n; b++; } s++; } free (sectors); return res; } libfreefare-0.4.0/libfreefare/mifare_classic.3000066400000000000000000000202001225640673100212570ustar00rootroot00000000000000.\" Copyright (C) 2010 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id$ .\" .Dd March 30, 2010 .Dt MIFARE_CLASSIC 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm mifare_classic_connect , .Nm mifare_classic_disconnect , .Nm mifare_classic_authenticate , .Nm mifare_classic_read , .Nm mifare_classic_init_value , .Nm mifare_classic_read_value , .Nm mifare_classic_write , .Nm mifare_classic_increment , .Nm mifare_classic_decrement , .Nm mifare_classic_restore , .Nm mifare_classic_transfer , .Nm mifare_classic_get_trailer_block_permission , .Nm mifare_classic_get_data_block_permission , .Nm mifare_classic_format_sector , .Nm mifare_classic_trailer_block , .Nm mifare_classic_block_sector , .Nm mifare_classic_sector_first_block , .Nm mifare_classic_sector_block_count , .Nm mifare_classic_sector_last_block .Nd Mifare Classic Manipulation Functions .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft int .Fn mifare_classic_connect "MifareTag tag" .Ft int .Fn mifare_classic_disconnect "MifareTag tag" .Ft int .Fn mifare_classic_authenticate "MifareTag tag" "const MifareClassicBlockNumber block" "const MifareClassicKey key" "const MifareClassicKeyType key_type" .Ft int .Fn mifare_classic_read "MifareTag tag" "const MifareClassicBlockNumber block" "MifareClassicBlock *data" .Ft int .Fn mifare_classic_init_value "MifareTag tag" "const MifareClassicBlockNumber block" "const int32_t value" "const MifareClassicBlockNumber adr" .Ft int .Fn mifare_classic_read_value "MifareTag tag" "const MifareClassicBlockNumber block" "int32_t *value" "MifareClassicBlockNumber *adr" .Ft int .Fn mifare_classic_write "MifareTag tag" "const MifareClassicBlockNumber block" "const MifareClassicBlock data" .Ft int .Fn mifare_classic_increment "MifareTag tag" "const MifareClassicBlockNumber block" "const uint32_t amount" .Ft int .Fn mifare_classic_decrement "MifareTag tag" "const MifareClassicBlockNumber block" "const uint32_t amount" .Ft int .Fn mifare_classic_restore "MifareTag tag" "const MifareClassicBlockNumber block" .Ft int .Fn mifare_classic_transfer "MifareTag tag" "const MifareClassicBlockNumber block" .Ft int .Fn mifare_classic_get_trailer_block_permission "MifareTag tag" "const MifareClassicBlockNumber block" "const uint16_t permission" "const MifareClassicKeyType key_type" .Ft int .Fn mifare_classic_get_data_block_permission "MifareTag tag" "const MifareClassicBlockNumber block" "const unsigned char permission" "const MifareClassicKeyType key_type" .Ft int .Fn mifare_classic_format_sector "MifareTag tag" "const MifareClassicSectorNumber sector" .Ft void .Fn mifare_classic_trailer_block "MifareClassicBlock *block" "const MifareClassicKey key_a" "const uint8_t ab_0" "const uint8_t ab_1" "const uint8_t ab_2" "const uint8_t ab_tb" "const uint8_t gpb" "const MifareClassicKey key_b" .Ft MifareClassicSectorNumber .Fn mifare_classic_block_sector "MifareClassicBlockNumber block" .Ft MifareClassicBlockNumber .Fn mifare_classic_sector_first_block "MifareClassicSectorNumber sector" .Ft size_t .Fn mifare_classic_sector_block_count "MifareClassicSectorNumber sector" .Ft MifareClassicBlockNumber .Fn mifare_classic_sector_last_block "MifareClassicSectorNumber sector" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION The .Fn mifare_classic_* set of functions allow manipulation of both Mifare Classic 1k and Mifare Classic 4k cards. .Pp A given .Vt tag is activated using .Fn mifare_classic_connect , deactivation is performed with .Fn mifare_classic_disconnect . .Pp After a successful connection, an authentication for a .Vt block using a .Vt key of type .Vt key_type (either .Ar MFC_KEY_A or .Ar MFC_KEY_B ) using .Fn mifare_classic_authenticate is required for further operation. .Pp Once successfuly authenticated, .Vt data of a .Vt block can be read using .Fn mifare_classic_read and written using .Fn mifare_classic_write . .Pp Value-blocks can be easily accessed using the .Fn mifare_classic_read_value and .Fn mifare_classic_init_value functions. Additional functions are available for such .Vt blocks : .Fn mifare_classic_increment and .Fn mifare_classic_decrement increment or decrement the value of the provided .Vt block by .Vt amount and store the result in the target's internal data register. The value in this register can also be loaded with the value from another .Vt block using .Fn mifare_classic_restore . Writing back the value of the target's internal data register to a .Vt block is requested using .Fn mifare_classic_transfer . .Pp Permissions for a data .Vt block can be fetched using .Fn mifare_classic_get_data_block_permission . The access .Vt permission to be checkf for key .Vt key_type is one of .Ar MCAB_R , .Ar MCAB_W , .Ar MCAB_I or .Ar MCAB_D ; respectively for read, write, increment and decrement permissions. .Pp .Fn mifare_classic_get_trailer_block_permission acts like .Fn mifare_classic_get_data_block_permission but the available .Vt permissions are .Ar MCAB_READ_KEYA , .Ar MCAB_READ_KEYB , .Ar MCAB_READ_ACCESS_BITS , .Ar MCAB_WRITE_ACCESS_BITS , .Ar MCAB_READ_KEYB and .Ar MCAB_WRITE_KEYB . .Pp A whole sector can be reset to factory defaults using .Fn mifare_classic_format_sector . .Pp The .Fn mifare_classic_trailer_block is a convenience function for building a trailer block .Vt block given a A key .Vt key_a ; access bits for the blocks of the sector .Vt ab_0 , .Vt ab_1 and .Vt ab_2 ; and a B key .Vt key_b . .Pp The .Fn mifare_classic_block_sector , function returns the number of the sector holding .Va block . .Pp The .Fn mifare_classic_sector_first_block , .Fn mifare_classic_sector_block_count , .Fn mifare_classic_sector_last_block or the complement of the .Fn mifare_classic_block_sector function and return various block-level information about the provided .Va sector . .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES Unless stated otherwise, all functions return a value greater than or equal to .Va 0 on success or .Va -1 on failure. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr freefare 3 , .Xr mad 3 .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@blogreen.org .An Romuald Conty Aq romuald@libnfc.org libfreefare-0.4.0/libfreefare/mifare_classic.c000066400000000000000000000547201225640673100213550ustar00rootroot00000000000000/*- * Copyright (C) 2009, 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ /* * This implementation was written based on information provided by the * following documents: * * MIFARE Standard Card IC * MF1ICS50 Functional specification * Rev. 5.3 - 29 January 2008 * * MIFARE Standard 4kByte Card IC * MF1ICS70 Functional specification * Rev. 4.1 - 29 January 2008 * * Making the Best of Mifare Classic * Wouter Teepe (Radboud University Nijmegen) * October 6, 2008 * * Mifare Std as NFC Forum Enabled Tag * Extensions for Mifare standard 1k/4k as NFC Forum Enable Tag * Rev. 1.1 — 21 August 2007 */ #include "config.h" #if defined(HAVE_SYS_TYPES_H) # include #endif #if defined(HAVE_SYS_ENDIAN_H) # include #endif #if defined(HAVE_ENDIAN_H) # include #endif #if defined(HAVE_COREFOUNDATION_COREFOUNDATION_H) # include #endif #if defined(HAVE_BYTESWAP_H) # include #endif #include #include #include #include #ifdef WITH_DEBUG # include #endif #include #include "freefare_internal.h" #define MC_OK 0x0A #define MC_AUTH_A 0x60 #define MC_AUTH_B 0x61 #define MC_READ 0x30 #define MC_WRITE 0xA0 #define MC_TRANSFER 0xB0 #define MC_DECREMENT 0xC0 #define MC_INCREMENT 0xC1 #define MC_RESTORE 0xC2 #define CLASSIC_TRANSCEIVE(tag, msg, res) CLASSIC_TRANSCEIVE_EX(tag, msg, res, 0) #define CLASSIC_TRANSCEIVE_EX(tag, msg, res, disconnect) \ do { \ errno = 0; \ DEBUG_XFER (msg, __##msg##_n, "===> "); \ int _res; \ if ((_res = nfc_initiator_transceive_bytes (tag->device, msg, __##msg##_n, res, __##res##_size, 0)) < 0) { \ if (disconnect) { \ tag->active = false; \ } \ return errno = EIO, -1; \ } \ __##res##_n = _res; \ DEBUG_XFER (res, __##res##_n, "<=== "); \ } while (0) /* Public Key A value of NFC Forum sectors */ const MifareClassicKey mifare_classic_nfcforum_public_key_a = { 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7 }; union mifare_classic_block { unsigned char data[16]; struct { uint32_t value; uint32_t value_; uint32_t value__; MifareClassicBlockNumber address; MifareClassicBlockNumber address_; MifareClassicBlockNumber address__; MifareClassicBlockNumber address___; } value; struct { MifareClassicKey key_a; uint8_t access_bits[3]; uint8_t gpb; MifareClassicKey key_b; } trailer; }; typedef unsigned char MifareClassicAccessBits; unsigned char mifare_data_access_permissions[] = { /* * [ Key A ] [ Key B ] * | | * ,----------- r(ead) | * |,---------- w(rite) | * ||,--------- d(ecrement) | * |||,-------- i(ncrement) | * |||| | * |||| ,------------------------ r * ,----- C3 |||| |,----------------------- w * |,---- C2 |||| ||,---------------------- d * ||,--- C1 |||| |||,--------------------- i * ||| |||| |||| * 0b000 0b 1111 1111 */ 0xff, /* Default (blank card) */ /* 0b001 0b 1000 1100 */ 0x8c, /* 0b010 0b 1000 1000 */ 0x88, /* 0b011 0b 1010 1111 */ 0xaf, /* 0b100 0b 1010 1010 */ 0xaa, /* 0b101 0b 0000 1000 */ 0x08, /* 0b110 0b 0000 1100 */ 0x0c, /* 0b111 0b 0000 0000 */ 0x00 }; uint16_t mifare_trailer_access_permissions[] = { /* * [ Key A ] [ Access bits ] [ Key B ] * | | | * ,----------- read A | | * |,---------- read B | | * ||,--------- write A | | * |||,-------- write B | | * |||| | | * |||| ,----------------------- read A | * |||| |,---------------------- read B | * |||| ||,--------------------- write A | * |||| |||,-------------------- write B | * |||| |||| | * |||| |||| ,----------------------------------- read A * ,----- C3 |||| |||| |,---------------------------------- read B * |,---- C2 |||| |||| ||,--------------------------------- write A * ||,--- C1 |||| |||| |||,-------------------------------- write B * ||| |||| |||| |||| * 0b000 0b 0010 1000 1010*/ 0x28a, /* 0b001 0b 0001 1100 0001*/ 0x1c1, /* 0b010 0b 0000 1000 1000*/ 0x088, /* 0b011 0b 0000 1100 0000*/ 0x0c0, /* 0b100 0b 0010 1010 1010*/ 0x2aa, /* Default (blank card) */ /* 0b101 0b 0000 1101 0000*/ 0x0d0, /* 0b110 0b 0001 1101 0001*/ 0x1d1, /* 0b111 0b 0000 1100 0000*/ 0x0c0 }; /* * Private functions */ int get_block_access_bits_shift (MifareClassicBlockNumber block, MifareClassicBlockNumber trailer); int get_block_access_bits (MifareTag tag, const MifareClassicBlockNumber block, MifareClassicAccessBits *block_access_bits); /* * Memory management functions. */ /* * Allocates and initialize a MIFARE Classic tag. */ MifareTag mifare_classic_tag_new (void) { return malloc (sizeof (struct mifare_classic_tag)); } /* * Free the provided tag. */ void mifare_classic_tag_free (MifareTag tag) { free (tag); } /* * MIFARE card communication preparation functions * * The following functions send NFC commands to the initiator to prepare * communication with a MIFARE card, and perform required cleanups after using * the target. */ /* * Establish connection to the provided tag. */ int mifare_classic_connect (MifareTag tag) { ASSERT_INACTIVE (tag); ASSERT_MIFARE_CLASSIC (tag); nfc_target pnti; nfc_modulation modulation = { .nmt = NMT_ISO14443A, .nbr = NBR_106 }; if (nfc_initiator_select_passive_target (tag->device, modulation, tag->info.abtUid, tag->info.szUidLen, &pnti) >= 0) { tag->active = 1; } else { errno = EIO; return -1; } return 0; } /* * Terminate connection with the provided tag. */ int mifare_classic_disconnect (MifareTag tag) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_CLASSIC (tag); if (nfc_initiator_deselect_target (tag->device) >= 0) { tag->active = 0; } else { errno = EIO; return -1; } return 0; } /* * Card manipulation functions * * The following functions perform direct communication with the connected * MIFARE card. */ /* * Send an authentification command to the provided MIFARE target. */ int mifare_classic_authenticate (MifareTag tag, const MifareClassicBlockNumber block, const MifareClassicKey key, const MifareClassicKeyType key_type) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_CLASSIC (tag); BUFFER_INIT (cmd, 12); BUFFER_INIT (res, 1); if (key_type == MFC_KEY_A) BUFFER_APPEND (cmd, MC_AUTH_A); else BUFFER_APPEND (cmd, MC_AUTH_B); BUFFER_APPEND(cmd, block); BUFFER_APPEND_BYTES (cmd, key, 6); // To support both 4-byte & 7-byte UID cards: BUFFER_APPEND_BYTES (cmd, tag->info.abtUid + tag->info.szUidLen - 4, 4); CLASSIC_TRANSCEIVE_EX (tag, cmd, res, 1); MIFARE_CLASSIC(tag)->cached_access_bits.sector_trailer_block_number = -1; MIFARE_CLASSIC(tag)->cached_access_bits.sector_access_bits = 0x00; MIFARE_CLASSIC(tag)->last_authentication_key_type = key_type; return (BUFFER_SIZE (res) == 0) ? 0 : res[0]; } /* * Read data from the provided MIFARE target. */ int mifare_classic_read (MifareTag tag, const MifareClassicBlockNumber block, MifareClassicBlock *data) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_CLASSIC (tag); BUFFER_INIT (cmd, 2); BUFFER_ALIAS (res, data, sizeof(MifareClassicBlock)); BUFFER_APPEND (cmd, MC_READ); BUFFER_APPEND (cmd, block); CLASSIC_TRANSCEIVE (tag, cmd, res); return 0; } int mifare_classic_init_value (MifareTag tag, const MifareClassicBlockNumber block, const int32_t value, const MifareClassicBlockNumber adr) { union mifare_classic_block b; uint32_t le_value = htole32 ((uint32_t)value); b.value.value = le_value; b.value.value_ = ~le_value; b.value.value__ = le_value; b.value.address = adr; b.value.address_ = ~adr; b.value.address__ = adr; b.value.address___ = ~adr; if (mifare_classic_write (tag, block, b.data) < 0) return -1; return 0; } int mifare_classic_read_value (MifareTag tag, const MifareClassicBlockNumber block, int32_t *value, MifareClassicBlockNumber *adr) { union mifare_classic_block b; if (mifare_classic_read (tag, block, &b.data) < 0) return -1; if ((b.value.value ^ (uint32_t)~b.value.value_) || (b.value.value != b.value.value__)) { errno = EIO; return -1; } if ((b.value.address ^ (uint8_t)~b.value.address_) || (b.value.address != b.value.address__) || (b.value.address_ != b.value.address___)) { errno = EIO; return -1; } if (value) *value = le32toh (b.value.value); if (adr) *adr = b.value.address; return 0; } /* * Write data to the provided MIFARE target. */ int mifare_classic_write (MifareTag tag, const MifareClassicBlockNumber block, const MifareClassicBlock data) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_CLASSIC (tag); BUFFER_INIT (cmd, 2 + sizeof (MifareClassicBlock)); BUFFER_INIT (res, 1); BUFFER_APPEND (cmd, MC_WRITE); BUFFER_APPEND (cmd, block); BUFFER_APPEND_BYTES (cmd, data, sizeof (MifareClassicBlock)); CLASSIC_TRANSCEIVE (tag, cmd, res); return (BUFFER_SIZE (res) == 0) ? 0 : res[0]; } /* * Increment the given value block by the provided amount into the internal * data register. */ int mifare_classic_increment (MifareTag tag, const MifareClassicBlockNumber block, const uint32_t amount) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_CLASSIC (tag); BUFFER_INIT (cmd, 6); BUFFER_INIT (res, 1); BUFFER_APPEND (cmd, MC_INCREMENT); BUFFER_APPEND (cmd, block); BUFFER_APPEND_LE (cmd, amount, 4, sizeof (amount)); CLASSIC_TRANSCEIVE (tag, cmd, res); return (BUFFER_SIZE (res) == 0) ? 0 : res[0]; } /* * Decrement the given value block by the provided amount into the internal * data register. */ int mifare_classic_decrement (MifareTag tag, const MifareClassicBlockNumber block, const uint32_t amount) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_CLASSIC (tag); BUFFER_INIT (cmd, 6); BUFFER_INIT (res, 1); BUFFER_APPEND (cmd, MC_DECREMENT); BUFFER_APPEND (cmd, block); BUFFER_APPEND_LE (cmd, amount, 4, sizeof (amount)); CLASSIC_TRANSCEIVE (tag, cmd, res); return (BUFFER_SIZE (res) == 0) ? 0 : res[0]; } /* * Store the provided block to the internal data register. */ int mifare_classic_restore (MifareTag tag, const MifareClassicBlockNumber block) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_CLASSIC (tag); /* * Same length as the increment and decrement commands but only the first * two bytes are actually used. The 4 bytes after the block number are * meaningless but required (NULL-filled). */ BUFFER_INIT (cmd, 6); BUFFER_INIT (res, 1); BUFFER_APPEND (cmd, MC_RESTORE); BUFFER_APPEND (cmd, block); BUFFER_APPEND (cmd, 0x00); BUFFER_APPEND (cmd, 0x00); BUFFER_APPEND (cmd, 0x00); BUFFER_APPEND (cmd, 0x00); CLASSIC_TRANSCEIVE (tag, cmd, res); return (BUFFER_SIZE (res) == 0) ? 0 : res[0]; } /* * Store the internal data register to the provided block. */ int mifare_classic_transfer (MifareTag tag, const MifareClassicBlockNumber block) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_CLASSIC (tag); BUFFER_INIT (cmd, 2); BUFFER_INIT (res, 1); BUFFER_APPEND (cmd, MC_TRANSFER); BUFFER_APPEND (cmd, block); CLASSIC_TRANSCEIVE (tag, cmd, res); /* * Depending on the device we are using, on success, the TRANSFER command * returns either no data (e.g. touchatag) or a 1 byte response, 0x0A, * meaning that the action was performed correctly (e.g. Snapper Feeder, * SCL 3711). */ if (!BUFFER_SIZE (res) || ((BUFFER_SIZE (res) == 1) && (res[0] = MC_OK))) return 0; else return res[0]; } /* * Access bit manipulation functions * * The following functions provide a convenient API for reading MIFARE card * access bits. A cache system makes these functions query a single time the * MIFARE card regardless of the number of information requested between two * authentications (i.e. for the current sector). */ /* * Given a block, determine the rank of applicable access bits in the trailer * block. * * For 4 blocks sectors, each access bit applies to a single block; but for 16 * blocks sectors (second part of MIFARE Classic 4k), the first 3 access bits * apply to 5 blocks, and the last access bits apply to the trailer block: * * Sector | Access bits | Shift * -------------------------------------+--------------+------- * 4, 128, 129, 130, 131, 132, 144 | ---x---x---x | 0 * 1, 5, 133, 134, 135, 136, 137 | --x---x---x- | 1 * 2, 6, 138, 138, 140, 141, 142 | -x---x---x-- | 2 * 3, 7, 143 | x---x---x--- | 3 * */ int get_block_access_bits_shift (MifareClassicBlockNumber block, MifareClassicBlockNumber trailer) { if (block == trailer) { return 3; } else { if (block < 128) return block % 4; else return ((block - 128) % 16) / 5; } } /* * Fetch access bits for a given block from the block's sector's trailing * block. */ int get_block_access_bits (MifareTag tag, const MifareClassicBlockNumber block, MifareClassicAccessBits *block_access_bits) { /* * The first block which holds the manufacturer block seems to have * inconsistent access bits. */ if (block == 0) { errno = EINVAL; return -1; } uint16_t sector_access_bits, sector_access_bits_; MifareClassicBlockNumber trailer = mifare_classic_sector_last_block (mifare_classic_block_sector (block)); /* * The trailer block contains access bits for the whole sector in a 3 bytes * structure that holds 2 times the permissions (once inverted, once * not-inverted). * * First we get these bytes, and check the inverted and non-inverted * permissions match. A cache mechanism prevents many read access to the * NFC target if the function is called multiple times on the same block. */ if (MIFARE_CLASSIC(tag)->cached_access_bits.sector_trailer_block_number == trailer) { /* cache hit! */ sector_access_bits = MIFARE_CLASSIC(tag)->cached_access_bits.sector_access_bits; } else { MifareClassicBlock trailer_data; if (mifare_classic_read (tag, trailer, &trailer_data) < 0) { return -1; } sector_access_bits_ = trailer_data[6] | ((trailer_data[7] & 0x0f) << 8) | 0xf000; sector_access_bits = ((trailer_data[7] & 0xf0) >> 4) | (trailer_data[8] << 4); if (sector_access_bits ^ (uint16_t)~sector_access_bits_) { /* Sector locked */ errno = EIO; return -1; } MIFARE_CLASSIC(tag)->cached_access_bits.sector_trailer_block_number = trailer; MIFARE_CLASSIC(tag)->cached_access_bits.block_number = -1; MIFARE_CLASSIC(tag)->cached_access_bits.sector_access_bits = sector_access_bits; } /* * To ease permissions lookup, related permission bits which are not * contiguous are assembled in a quartet. */ if (MIFARE_CLASSIC(tag)->cached_access_bits.block_number == block) { /* cache hit! */ *block_access_bits = MIFARE_CLASSIC(tag)->cached_access_bits.block_access_bits; } else { *block_access_bits = 0; /* ,-------C3 * |,------C2 * ||,---- C1 * ||| */ uint16_t block_access_bits_mask = 0x0111 << get_block_access_bits_shift (block, trailer); /* ||| * ||`---------------. * |`---------------.| * `---------------.|| * ||| */ if (sector_access_bits & block_access_bits_mask & 0x000f) *block_access_bits |= 0x01; /* C1 */ if (sector_access_bits & block_access_bits_mask & 0x00f0) *block_access_bits |= 0x02; /* C2 */ if (sector_access_bits & block_access_bits_mask & 0x0f00) *block_access_bits |= 0x04; /* C3 */ MIFARE_CLASSIC(tag)->cached_access_bits.block_number = block; MIFARE_CLASSIC(tag)->cached_access_bits.block_access_bits = *block_access_bits; } return 0; } /* * Get information about the trailer block. */ int mifare_classic_get_trailer_block_permission (MifareTag tag, const MifareClassicBlockNumber block, const uint16_t permission, const MifareClassicKeyType key_type) { MifareClassicAccessBits access_bits; if (get_block_access_bits (tag, block, &access_bits) < 0) { return -1; } if (MIFARE_CLASSIC(tag)->cached_access_bits.sector_trailer_block_number == block) { return (mifare_trailer_access_permissions[access_bits] & (permission) << ((key_type == MFC_KEY_A) ? 1 : 0)) ? 1 : 0; } else { errno = EINVAL; return -1; } } /* * Get information about data blocks. */ int mifare_classic_get_data_block_permission (MifareTag tag, const MifareClassicBlockNumber block, const unsigned char permission, const MifareClassicKeyType key_type) { MifareClassicAccessBits access_bits; if (get_block_access_bits (tag, block, &access_bits) < 0) { return -1; } if (MIFARE_CLASSIC(tag)->cached_access_bits.sector_trailer_block_number != block) { return ((mifare_data_access_permissions[access_bits] & (permission << ( (key_type == MFC_KEY_A) ? 4 : 0 ))) ? 1 : 0); } else { errno = EINVAL; return -1; } } /* * Miscellaneous functions */ /* * Reset a MIFARE target sector to factory default. */ int mifare_classic_format_sector (MifareTag tag, const MifareClassicSectorNumber sector) { MifareClassicBlockNumber first_sector_block = mifare_classic_sector_first_block (sector); MifareClassicBlockNumber last_sector_block = mifare_classic_sector_last_block (sector); /* * Check that the current key allow us to rewrite data and trailer blocks. */ if (first_sector_block == 0) { /* First block is read-only */ first_sector_block = 1; } for (int n = first_sector_block; n < last_sector_block; n++) { if (mifare_classic_get_data_block_permission(tag, n, MCAB_W, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) { return errno = EPERM, -1; } } if ((mifare_classic_get_trailer_block_permission(tag, last_sector_block, MCAB_WRITE_KEYA, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) || (mifare_classic_get_trailer_block_permission(tag, last_sector_block, MCAB_WRITE_ACCESS_BITS, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) || (mifare_classic_get_trailer_block_permission(tag, last_sector_block, MCAB_WRITE_KEYB, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1)) { return errno = EPERM, -1; } MifareClassicBlock empty_data_block; memset (empty_data_block, 0, sizeof (empty_data_block)); MifareClassicBlock default_trailer_block = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Key A */ 0xff, 0x07, 0x80, /* Access bits */ 0x69, /* GPB */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* Key B */ }; for (int n = first_sector_block; n < last_sector_block; n++) { if (mifare_classic_write (tag, n, empty_data_block) < 0) { return errno = EIO, -1; } } if (mifare_classic_write (tag, last_sector_block, default_trailer_block) < 0) { return errno = EIO, -1; } return 0; } MifareClassicSectorNumber mifare_classic_block_sector (MifareClassicBlockNumber block) { MifareClassicSectorNumber res; if (block < 32 * 4) res = block / 4; else res = 32 + ( (block - (32 * 4)) / 16 ); return res; } /* * Get the sector's first block number */ MifareClassicBlockNumber mifare_classic_sector_first_block (MifareClassicSectorNumber sector) { int res; if (sector < 32) { res = sector * 4; } else { res = 32 * 4 + (sector - 32) * 16; } return res; } size_t mifare_classic_sector_block_count (MifareClassicSectorNumber sector) { return (sector < 32) ? 4 : 16 ; } /* * Get the sector's last block number (aka trailer block) */ MifareClassicBlockNumber mifare_classic_sector_last_block (MifareClassicSectorNumber sector) { return mifare_classic_sector_first_block (sector) + mifare_classic_sector_block_count (sector) - 1; } /* * Generates a MIFARE trailer block. */ void mifare_classic_trailer_block (MifareClassicBlock *block, const MifareClassicKey key_a, uint8_t ab_0, uint8_t ab_1, uint8_t ab_2, uint8_t ab_tb, const uint8_t gpb, const MifareClassicKey key_b) { union mifare_classic_block *b = (union mifare_classic_block *)block; // *((union mifare_classic_block *)(&block)); ab_0 = DB_AB(ab_0); ab_1 = DB_AB(ab_1); ab_2 = DB_AB(ab_2); ab_tb = TB_AB(ab_tb); memcpy (b->trailer.key_a, key_a, sizeof (MifareClassicKey)); uint32_t access_bits = ((((( ab_0 & 0x4) >> 2) << 8) | (((ab_0 & 0x2) >> 1) << 4) | (ab_0 & 0x1)) | (((((ab_1 & 0x4) >> 2) << 8) | (((ab_1 & 0x2) >> 1) << 4) | (ab_1 & 0x1)) << 1) | (((((ab_2 & 0x4) >> 2) << 8) | (((ab_2 & 0x2) >> 1) << 4) | (ab_2 & 0x1)) << 2) | (((((ab_tb & 0x4) >> 2) << 8) | (((ab_tb & 0x2) >> 1) << 4) | (ab_tb & 0x1)) << 3)); uint32_t access_bits_ = ((~access_bits) & 0x00000fff); uint32_t ab = htole32(((access_bits << 12) | access_bits_)); memcpy (&(b->trailer.access_bits), &ab, 3); b->trailer.gpb = gpb; memcpy (b->trailer.key_b, key_b, sizeof (MifareClassicKey)); } libfreefare-0.4.0/libfreefare/mifare_desfire.3000066400000000000000000000521331225640673100212710ustar00rootroot00000000000000.\" Copyright (C) 2010,2011 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id$ .\" .Dd March 26, 2011 .Dt MIFARE_DESFIRE 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm mifare_desfire_connect , .Nm mifare_desfire_disconnect . .\" .Nm mifare_desfire_authenticate , .Nm mifare_desfire_authenticate_aes , .Nm mifare_desfire_authenticate_iso , .Nm mifare_desfire_change_key_settings , .Nm mifare_desfire_get_key_settings , .Nm mifare_desfire_change_key , .Nm mifare_desfire_get_key_version , .\" .Nm mifare_desfire_create_application , .Nm mifare_desfire_create_application_3k3des , .Nm mifare_desfire_create_application_aes , .Nm mifare_desfire_create_application_iso , .Nm mifare_desfire_create_application_3k3des_iso , .Nm mifare_desfire_create_application_aes_iso , .Nm mifare_desfire_delete_application , .Nm mifare_desfire_get_application_ids , .Nm mifare_desfire_free_application_ids , .Nm mifare_desfire_get_df_names , .Nm mifare_desfire_select_application , .\" .Nm mifare_desfire_format_picc , .\" .Nm mifare_desfire_get_version , .Nm mifare_desfire_free_mem , .Nm mifare_desfire_set_configuration , .Nm mifare_desfire_set_default_key , .Nm mifare_desfire_set_ats , .Nm mifare_desfire_get_card_uid , .\" .Nm mifare_desfire_get_file_ids , .Nm mifare_desfire_get_iso_file_ids , .Nm mifare_desfire_get_file_settings , .Nm mifare_desfire_change_file_settings , .Nm mifare_desfire_create_std_data_file , .Nm mifare_desfire_create_std_data_file_iso , .Nm mifare_desfire_create_backup_data_file , .Nm mifare_desfire_create_backup_data_file_iso , .Nm mifare_desfire_create_value_file , .Nm mifare_desfire_create_linear_record_file , .Nm mifare_desfire_create_linear_record_file_iso , .Nm mifare_desfire_create_cyclic_record_file , .Nm mifare_desfire_create_cyclic_record_file_iso , .Nm mifare_desfire_delete_file , .\" .Nm mifare_desfire_read_data , .Nm mifare_desfire_read_data_ex , .Nm mifare_desfire_write_data , .Nm mifare_desfire_write_data_ex , .Nm mifare_desfire_get_value , .Nm mifare_desfire_get_value_ex , .Nm mifare_desfire_credit , .Nm mifare_desfire_credit_ex , .Nm mifare_desfire_debit , .Nm mifare_desfire_debit_ex , .Nm mifare_desfire_limited_credit , .Nm mifare_desfire_limited_credit_ex , .Nm mifare_desfire_write_record , .Nm mifare_desfire_write_record_ex , .Nm mifare_desfire_read_records , .Nm mifare_desfire_read_records_ex , .Nm mifare_desfire_clear_record_file , .Nm mifare_desfire_commit_transaction , .Nm mifare_desfire_abort_transaction , .Nd Mifare DESFire Manipulation Functions .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft int .Fn mifare_desfire_connect "MifareTag tag" .Ft int .Fn mifare_desfire_disconnect "MifareTag tag" .Ft int .Fn mifare_desfire_authenticate "MifareTag tag" "uint8_t key_no" "MifareDESFireKey key" .Ft int .Fn mifare_desfire_authenticate_aes "MifareTag tag" "uint8_t key_no" "MifareDESFireKey key" .Ft int .Fn mifare_desfire_authenticate_iso "MifareTag tag" "uint8_t key_no" "MifareDESFireKey key" .Ft int .Fn mifare_desfire_change_key_settings "MifareTag tag" "uint8_t settings" .Ft int .Fn mifare_desfire_get_key_settings "MifareTag tag" "uint8_t *settings" "uint8_t *max_keys" .Ft int .Fn mifare_desfire_change_key "MifareTag tag" "uint8_t key_no" "MifareDESFireKey new_key" "MifareDESFireKey old_key" .Ft int .Fn mifare_desfire_get_key_version "MifareTag tag" "uint8_t key_no" "uint8_t *version" .Ft int .Fn mifare_desfire_create_application "MifareTag tag" "MifareDESFireAID aid" "uint8_t settings" "uint8_t key_no" .Ft int .Fn mifare_desfire_create_application_aes "MifareTag tag" "MifareDESFireAID aid" "uint8_t settings" "uint8_t key_no" .Ft int .Fn mifare_desfire_create_application_3k3des "MifareTag tag" "MifareDESFireAID aid" "uint8_t settings" "uint8_t key_no" .Ft int .Fn mifare_desfire_create_application_iso "MifareTag tag" "MifareDESFireAID aid" "uint8_t settings" "uint8_t key_no" "int want_iso_file_identifiers" "uint16_t iso_file_id" "uint8_t *iso_file_name" "size_t iso_file_name_len" .Ft int .Fn mifare_desfire_create_application_3k3des_iso "MifareTag tag" "MifareDESFireAID aid" "uint8_t settings" "uint8_t key_no" "int want_iso_file_identifiers" "uint16_t iso_file_id" "uint8_t *iso_file_name" "size_t iso_file_name_len" .Ft int .Fn mifare_desfire_create_application_aes_iso "MifareTag tag" "MifareDESFireAID aid" "uint8_t settings" "uint8_t key_no" "int want_iso_file_identifiers" "uint16_t iso_file_id" "uint8_t *iso_file_name" "size_t iso_file_name_len" .Ft int .Fn mifare_desfire_delete_application "MifareTag tag" "MifareDESFireAID aid" .Ft int .Fn mifare_desfire_get_application_ids "MifareTag tag" "MifareDESFireAID *aids[]" "size_t *count" .Ft void .Fn mifare_desfire_free_application_ids "MifareDESFireAID aids[]" .Ft int .Fn mifare_desfire_get_df_names "MifareTag tag" "MifareDESFireDF *dfs[]" "size_t *count" .Ft int .Fn mifare_desfire_select_application "MifareTag tag" "MifareDESFireAID aid" .Ft int .Fn mifare_desfire_format_picc "MifareTag tag" .Ft int .Fn mifare_desfire_get_version "MifareTag tag" "struct mifare_desfire_version_info *version_info" .Ft int .Fn mifare_desfire_free_mem "MifareTag tag" "uint32_t *size" .Ft int .Fn mifare_desfire_set_configuration "MifareTag tag" "bool disable_format" "bool enable_random_uid" .Ft int .Fn mifare_desfire_set_default_key "MifareTag tag" "MifareDESFireKey key" .Ft int .Fn mifare_desfire_set_ats "MifareTag tag" "uint8_t *ats" .Ft int .Fn mifare_desfire_get_card_uid "MifareTag tag" "char **uid" .Ft int .Fn mifare_desfire_get_file_ids "MifareTag tag" "uint8_t *files[]" "size_t *count" .Ft int .Fn mifare_desfire_get_iso_file_ids "MifareTag tag" "uint16_t *files[]" "size_t *count" .Ft int .Fn mifare_desfire_get_file_settings "MifareTag tag" "uint8_t file_no" "struct mifare_desfire_file_settings *settings" .Ft int .Fn mifare_desfire_change_file_settings "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" .Ft int .Fn mifare_desfire_create_std_data_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t file_size" .Ft int .Fn mifare_desfire_create_std_data_file_iso "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t file_size" "uint16_t iso_file_id" .Ft int .Fn mifare_desfire_create_backup_data_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t file_size" .Ft int .Fn mifare_desfire_create_backup_data_file_iso "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t file_size" "uint16_t iso_file_id" .Ft int .Fn mifare_desfire_create_value_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "int32_t lower_limit" "int32_t upper_limit" "int32_t value" "uint8_t limited_credit_enable" .Ft int .Fn mifare_desfire_create_linear_record_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t record_size" "uint32_t max_number_of_records" .Ft int .Fn mifare_desfire_create_linear_record_file_iso "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t record_size" "uint32_t max_number_of_records" .Ft int .Fn mifare_desfire_create_cyclic_record_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t record_size" "uint32_t max_number_of_records" "uint16_t iso_file_id" .Ft int .Fn mifare_desfire_create_cyclic_record_file_iso "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t record_size" "uint32_t max_number_of_records" "uint16_t iso_file_id" .Ft int .Fn mifare_desfire_delete_file "MifareTag tag" "uint8_t file_no" .Ft ssize_t .Fn mifare_desfire_read_data "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" .Ft ssize_t .Fn mifare_desfire_read_data_ex "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" "int cs" .Ft ssize_t .Fn mifare_desfire_write_data "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" .Ft ssize_t .Fn mifare_desfire_write_data_ex "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" "int cs" .Ft int .Fn mifare_desfire_get_value "MifareTag tag" "uint8_t file_no" "int32_t *value" .Ft int .Fn mifare_desfire_get_value_ex "MifareTag tag" "uint8_t file_no" "int32_t *value" "int cs" .Ft int .Fn mifare_desfire_credit "MifareTag tag" "uint8_t file_no" "int32_t amount" .Ft int .Fn mifare_desfire_credit_ex "MifareTag tag" "uint8_t file_no" "int32_t amount" "int cs" .Ft int .Fn mifare_desfire_debit "MifareTag tag" "uint8_t file_no" "int32_t amount" .Ft int .Fn mifare_desfire_debit_ex "MifareTag tag" "uint8_t file_no" "int32_t amount" "int cs" .Ft int .Fn mifare_desfire_limited_credit "MifareTag tag" "uint8_t file_no" "int32_t amount" .Ft int .Fn mifare_desfire_limited_credit_ex "MifareTag tag" "uint8_t file_no" "int32_t amount" "int cs" .Ft ssize_t .Fn mifare_desfire_write_record "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" .Ft ssize_t .Fn mifare_desfire_write_record_ex "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" "int cs" .Ft ssize_t .Fn mifare_desfire_read_records "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" .Ft ssize_t .Fn mifare_desfire_read_records_ex "MifareTag tag" "uint7_t file_no" "off_t offset" "size_t length" "void *data" "int cs" .Ft int .Fn mifare_desfire_clear_record_file "MifareTag tag" "uint8_t file_no" .Ft int .Fn mifare_desfire_commit_transaction "MifareTag tag" .Ft int .Fn mifare_desfire_abort_transaction "MifareTag tag" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION The .Fn mifare_desfire_* functions allows management of Mifare DESFire tags. .Pp .Ss Card-level operations The .Fn mifare_desfire_connect and .Fn mifare_desfire_disconnect functions activates and deactivates the provided .Vt tag . All .Fn mifare_desfire_* functions that operates on a .Vt tag require it to be on activated. .Pp After activation, the selected application is the master application. It is possible to select another application using the .Fn mifare_desfire_select_application function (see bellow). .Pp The .Fn mifare_desfire_get_version function retrieve various information about the provided .Vt tag , including UID, batch number, production date, and hardware and software information. Refer to the freefare.h header file for details about the .Vt version_info field. .Pp The .Fn mifare_desfire_free_mem functions returns the .Vt size of the free memory on the PICC (in bytes). .Pp The .Fn mifare_desfire_set_configuration function can be used to deactivate the format function when setting .Vt disable_format to a value different from 0, or swicth the card to use random UDI setting .Vt enable_random_uid to a non-zero value. .Pp The .Vt mifare_desfire_set_default_key function sets the .Vt key argument as the default key for new applications. .Pp The .Nm mifare_desfire_set_ats function replace the ATS bytes returned by PICC when it is selected. .Pp The .Nm mifare_desfire_get_card_uid function can be used with a PICC configured for using random UID to retrieve the original UID of the target. .Pp The .Fn mifare_desfire_format_picc function resets .Vt tag to factory defaults. For this function to work, a previous authentication with the card master key is required. .Pp .Ss Application-level operations The .Fn mifare_desfire_select_application function makes the application identified by .Vt aid the active one. Further file operations will be performed in the context of this application. After a call to .Vt mifare_desfire_connect , the default application is the card master application. It can be selected again calling the .Fn mifare_desfire_select_application function either with an .Vt aid with all its fields set to 0, or by providing the NULL .Vt aid . .Pp The .Fn mifare_desfire_authenticate function performs an authentication using the key number .Vt key_no on the card and the .Vt key (3)DES key on .Vt tag . .Pp The .Fn mifare_desfire_authenticate_aes function performs an authentication using an AES .Vt key . .Pp The .Fn mifare_desfire_authenticate_iso function performs an ISO authentication using either a 3DES or a 3K3DES .Vt key . .Pp The .Fn mifare_desfire_get_key_settings function, returns the .Vt settings and the number of keys .Vt max_keys of the selected application. .Pp The .Fn mifare_desfire_change_key_settings function changes the selected application settings to .Vt settings . The application number of keys cannot be changed after the application has been created. .Pp The .Fn mifare_desfire_change_key changes the key .Vt key_no from .Vt old_key to .Vt new_key on .Vt tag . Depending on the application settings, a previous authentication with the same key or another key may be required. .Pp The .Fn mifare_desfire_get_key_version function retrieves the .Vt version of the key with number .Vt key_no of the selected application. .Pp The .Fn mifare_desfire_create_application function, creates an application with AID .Vt aid , the .Vt settings key settings and .Vt key_no authentication keys. Authentication keys are set to 0 after creation. .Pp The .Fn mifare_desfire_create_application_3k3des and .Fn mifare_desfire_create_application_aes functions acts as the .Fn mifare_desfire_create_application function except that the whole application is configured to use 3K3DES or AES keys. It is possible to achive the same result using the .Fn mifare_desfire_create_application function and ORing the .Vt key_no argument with .Vt APPLICATION_CRYPTO_3K3DES or .Vt APPLICATION_CRYPTO_AES respectively. .Pp The .Fn mifare_desfire_create_application_iso acts as the .Fn mifare_desfire_create_application function but allows one to specify if the created files within the application shall have an ISO file identifier setting .Vt want_iso_file_identifiers to a non-NULL value, a DF can be provided using .Vt iso_file_id , as long as an optional file name .Vt iso_file_name of length .Vt iso_file_name_len (in bytes). .Pp The .Fn mifare_desfire_create_application_3k3des_iso and .Fn mifare_desfire_create_application_aes_iso function acts as the regular .Fn mifare_desfire_create_application_3k3des and .Fn mifare_desfire_create_application_aes functions, providing the same extensions ISO parameters of .Fn mifare_desfire_create_application_iso . .Pp The .Fn mifare_desfire_delete_application deletes the application identified by AID .Vt aid . .Pp The .Fn mifare_desfire_get_application_ids function returns a list of all applications of the card. The .Vt aids array has to be freed after usage calling .Fn mifare_desfire_free_application_ids . .Pp The .Fn mifare_desfire_get_df_names retrieves the list of DF .Vt dfs from .Vt tag and set .Vt count to the number of idems in the allocated array. Memory has to be freed by the user using .Xr free 3 . .Pp .Ss File-level operations The .Fn mifare_desfire_get_file_ids function returns the list of .Vt count files in the selected application as .Vt files . The memory allocated for .Vt files has to be reclaimed using .Xr free 3 . .Pp The .Fn mifare_desfire_get_iso_file_ids function returns the list of .Vt count file ISO identifiers as .Vt files . The memory allocated for .Vt files has to be reclaimed using .Xr free 3 . .Pp The .Fn mifare_desfire_get_file_settings function retrieves the .Vt settings of the file .Vt file_no of the selected application of .Vt tag . .Pp The .Fn mifare_desfire_change_file_settings function change the .Vt communication_settings and .Vt access_rights of the file .Vt file_no of the selected application of .Vt tag . .Pp The .Fn mifare_desfire_create_* family of functions create a new file .Vt file_no with the provided .Vt communication_settings and .Vt access_rights on .Vt tag. .Bl -tag -width indent .It Fn mifare_desfire_create_std_data_file creates a standard data file of size .Vt file_size . .It Fn mifare_desfire_create_backup_data_file creates a backup data file of size .Vt file_size . .It Fn mifare_desfire_create_value_file creates a value file of value .Vt value constrained in the range .Vt lower_limit .Vt upper_limit , and with the .Vt limited_credit_enable settings. .It Fn mifare_desfire_create_linear_record_file creates a linear record file that can hold .Vt max_number_of_records records of size .Vt record_size . .It Fn mifare_desfire_create_cyclic_record_file creates a cyclic record file that can hold .Vt max_number_of_records records of size .Vt record_size . .El .Pp The .Fn mifare_desfire_create_*_iso family of functions acts as the functions without the .Vt _iso suffix but provide an additionnal argument .Vt iso_file_id . .Pp The .Fn mifare_desfire_delete_file removes the file .Vt file_no from the selected application of .Vt tag . .Ss Data-level operations The .Fn mifare_desfire_read_data function reads .Vt length bytes of data from offset .Vt offset of the file .Vt file_no and copies it to .Vt data . If .Vt length is set to 0, the file is read to end. The function returns the number of bytes read. .Pp The .Fn mifare_desfire_write_data function writes .Vt length bytes of data from offset .Vt offset of the file .Vt file_no and copies it to .Vt data . The function returns the number of bytes written. .Pp The .Fn mifare_desfire_get_value reads the .Vt value of the file .Vt file_no of the selected application. .Pp The .Fn mifare_desfire_credit function adds .Vt amount to the value of the file .Vt file_no of the selected application. .Pp The .Fn mifare_desfire_debit function substracts .Vt amount to the value of the file .Vt file_no of the selected application. .Pp to the value of the file .Vt file_no of the selected application. .Pp The .Fn mifare_desfire_limited_credit function adds .Vt amount to the value of the file .Vt file_no of the selected application. .Pp The .Fn mifare_desfire_write_record function writes .Vt length records starting at record .Vt offset of .Vt data in the file .Vt file_no and returns the number of bytes written. .Pp The .Fn mifare_desfire_read_records function reads .Vt length records starting at record .Vt offset from the file .Vt file_no and copy them to .Vt data , returning the number of bytes read. .Pp The .Fn mifare_desfire_clear_record_file function erase all records from the file .Vt file_no of the selected application. .Pp The .Fn mifare_desfire_commit_transaction validates the set of pending changes on the .Vt tag , while the .Fn mifare_desfire_abort_transaction rollbacks the changes. .Pp All data-manipulation functions that read data from and write data to files come with an .Fn *_ex variant (e.g. .Fn mifare_desfire_read_data_ex ) which accepts an extra parameter .Vt cs that defines the communication settings to use. If not provided, the library will try to read-out this value from the file's configuration. Because reading this information may be denied, the .Fn *_ex variant of functions still allows using the library for advanced usage. .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES Unless stated otherwise, all other functions return a value greater than or equal to .Va 0 on success or .Va -1 on failure. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr freefare 3 .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@il4p.org libfreefare-0.4.0/libfreefare/mifare_desfire.c000066400000000000000000001510741225640673100213550ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ /* * This implementation was written based on information provided by the * following documents: * * Draft ISO/IEC FCD 14443-4 * Identification cards * - Contactless integrated circuit(s) cards * - Proximity cards * - Part 4: Transmission protocol * Final Committee Draft - 2000-03-10 * * http://ridrix.wordpress.com/2009/09/19/mifare-desfire-communication-example/ */ #include "config.h" #if defined(HAVE_SYS_TYPES_H) # include #endif #if defined(HAVE_SYS_ENDIAN_H) # include #endif #if defined(HAVE_ENDIAN_H) # include #endif #if defined(HAVE_COREFOUNDATION_COREFOUNDATION_H) # include #endif #if defined(HAVE_BYTESWAP_H) # include #endif #include #include #include #include #ifdef WITH_DEBUG # include #endif #include #include #include "freefare_internal.h" #pragma pack (push) #pragma pack (1) struct mifare_desfire_raw_file_settings { uint8_t file_type; uint8_t communication_settings; uint16_t access_rights; union { struct { uint8_t file_size[3]; } standard_file; struct { int32_t lower_limit; int32_t upper_limit; int32_t limited_credit_value; uint8_t limited_credit_enabled; } value_file; struct { uint8_t record_size[3]; uint8_t max_number_of_records[3]; uint8_t current_number_of_records[3]; } linear_record_file; } settings; }; #pragma pack (pop) #define MAX_APPLICATION_COUNT 28 #define MAX_FILE_COUNT 32 #define CMAC_LENGTH 8 static struct mifare_desfire_file_settings cached_file_settings[MAX_FILE_COUNT]; static bool cached_file_settings_current[MAX_FILE_COUNT]; static int authenticate (MifareTag tag, uint8_t cmd, uint8_t key_no, MifareDESFireKey key); static int create_file1 (MifareTag tag, uint8_t command, uint8_t file_no, int has_iso_file_id, uint16_t iso_file_id, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size); static int create_file2 (MifareTag tag, uint8_t command, uint8_t file_no, int has_iso_file_id, uint16_t iso_file_id, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records); static ssize_t write_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, const void *data, int cs); static ssize_t read_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, void *data, int cs); #define NOT_YET_AUTHENTICATED 255 #define ASSERT_AUTHENTICATED(tag) \ do { \ if (MIFARE_DESFIRE (tag)->authenticated_key_no == NOT_YET_AUTHENTICATED) { \ return errno = EINVAL, -1;\ } \ } while (0) /* * XXX: cs < 0 is a CommunicationSettings detection error. Other values are * user errors. We may need to distinguish them. */ #define ASSERT_CS(cs) \ do { \ if (cs < 0) { \ return errno = EINVAL, -1; \ } else if (cs == 0x02) { \ return errno = EINVAL, -1; \ } else if (cs > 0x03) { \ return errno = EINVAL, -1; \ } \ } while (0) #define ASSERT_NOT_NULL(argument) \ do { \ if (!argument) { \ return errno = EINVAL, -1; \ } \ } while (0) /* * Convenience macros. */ /* Max APDU sizes to be ISO encapsulated by DESFIRE_TRANSCEIVE() From MIFARE DESFire Functional specification: MAX_CAPDU_SIZE: "The length of the total wrapped DESFire command is not longer than 55 byte long." MAX_RAPDU_SIZE: 1 status byte + 59 bytes */ #define MAX_CAPDU_SIZE 55 #define MAX_RAPDU_SIZE 60 /* * Transmit the message msg to the NFC tag and receive the response res. The * response buffer's size is set according to the quantity of data received. * * The Mifare DESFire function return value which is returned at the end of the * response is copied at the beginning to match the PICC documentation. */ #define DESFIRE_TRANSCEIVE(tag, msg, res) \ DESFIRE_TRANSCEIVE2 (tag, msg, __##msg##_n, res) /* Native DESFire APDUs will be wrapped in ISO7816-4 APDUs: CAPDUs will be 5 bytes longer (CLA+P1+P2+Lc+Le) RAPDUs will be 1 byte longer (SW1 SW2 instead of 1 status byte) */ #define DESFIRE_TRANSCEIVE2(tag, msg, msg_len, res) \ do { \ static uint8_t __msg[MAX_CAPDU_SIZE + 5] = { 0x90, 0x00, 0x00, 0x00, 0x00, /* ..., */ 0x00 }; \ /* CLA INS P1 P2 Lc PAYLOAD LE*/ \ static uint8_t __res[MAX_RAPDU_SIZE + 1]; \ size_t __len = 5; \ errno = 0; \ if (!msg) return errno = EINVAL, -1; \ __msg[1] = msg[0]; \ if (msg_len > 1) { \ __len += msg_len; \ __msg[4] = msg_len - 1; \ memcpy (__msg + 5, msg + 1, msg_len - 1); \ } \ /* reply length */ \ __msg[__len-1] = 0x00; \ MIFARE_DESFIRE (tag)->last_picc_error = OPERATION_OK; \ MIFARE_DESFIRE (tag)->last_pcd_error = OPERATION_OK; \ DEBUG_XFER (__msg, __len, "===> "); \ int _res; \ if ((_res = nfc_initiator_transceive_bytes (tag->device, __msg, __len, __res, __##res##_size + 1, 0)) < 0) { \ return errno = EIO, -1; \ } \ __##res##_n = _res; \ DEBUG_XFER (__res, __##res##_n, "<=== "); \ res[__##res##_n-2] = __res[__##res##_n-1]; \ __##res##_n--; \ if ((1 == __##res##_n) && (ADDITIONAL_FRAME != res[__##res##_n-1]) && (OPERATION_OK != res[__##res##_n-1])) { \ return MIFARE_DESFIRE (tag)->last_picc_error = res[0], -1; \ } \ memcpy (res, __res, __##res##_n - 1); \ } while (0) /* * Miscellaneous low-level memory manipulation functions. */ static int32_t le24toh (uint8_t data[3]); static int madame_soleil_get_read_communication_settings (MifareTag tag, uint8_t file_no) { struct mifare_desfire_file_settings settings; if (mifare_desfire_get_file_settings (tag, file_no, &settings)) return -1; if ((MIFARE_DESFIRE (tag)->authenticated_key_no == MDAR_READ (settings.access_rights)) || (MIFARE_DESFIRE (tag)->authenticated_key_no == MDAR_READ_WRITE (settings.access_rights))) return settings.communication_settings; else return 0; } static int madame_soleil_get_write_communication_settings (MifareTag tag, uint8_t file_no) { struct mifare_desfire_file_settings settings; if (mifare_desfire_get_file_settings (tag, file_no, &settings)) return -1; if ((MIFARE_DESFIRE (tag)->authenticated_key_no == MDAR_WRITE (settings.access_rights)) || (MIFARE_DESFIRE (tag)->authenticated_key_no == MDAR_READ_WRITE (settings.access_rights))) return settings.communication_settings; else return 0; } static int32_t le24toh (uint8_t data[3]) { return (data[2] << 16) | (data[1] << 8) | data[0]; } /* * Memory management functions. */ /* * Allocates and initialize a MIFARE DESFire tag. */ MifareTag mifare_desfire_tag_new (void) { MifareTag tag; if ((tag= malloc (sizeof (struct mifare_desfire_tag)))) { MIFARE_DESFIRE (tag)->last_picc_error = OPERATION_OK; MIFARE_DESFIRE (tag)->last_pcd_error = OPERATION_OK; MIFARE_DESFIRE (tag)->session_key = NULL; MIFARE_DESFIRE (tag)->crypto_buffer = NULL; MIFARE_DESFIRE (tag)->crypto_buffer_size = 0; } return tag; } /* * Free the provided tag. */ void mifare_desfire_tag_free (MifareTag tag) { free (MIFARE_DESFIRE (tag)->session_key); free (MIFARE_DESFIRE (tag)->crypto_buffer); free (tag); } /* * MIFARE card communication preparation functions * * The following functions send NFC commands to the initiator to prepare * communication with a MIFARE card, and perform required cleannups after using * the target. */ /* * Establish connection to the provided tag. */ int mifare_desfire_connect (MifareTag tag) { ASSERT_INACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); nfc_target pnti; nfc_modulation modulation = { .nmt = NMT_ISO14443A, .nbr = NBR_106 }; if (nfc_initiator_select_passive_target (tag->device, modulation, tag->info.abtUid, tag->info.szUidLen, &pnti) >= 0) { tag->active = 1; free (MIFARE_DESFIRE (tag)->session_key); MIFARE_DESFIRE (tag)->session_key = NULL; MIFARE_DESFIRE (tag)->last_picc_error = OPERATION_OK; MIFARE_DESFIRE (tag)->last_pcd_error = OPERATION_OK; MIFARE_DESFIRE (tag)->authenticated_key_no = NOT_YET_AUTHENTICATED; MIFARE_DESFIRE (tag)->selected_application = 0; } else { errno = EIO; return -1; } return 0; } /* * Terminate connection with the provided tag. */ int mifare_desfire_disconnect (MifareTag tag) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); free (MIFARE_DESFIRE (tag)->session_key); MIFARE_DESFIRE(tag)->session_key = NULL; if (nfc_initiator_deselect_target (tag->device) >= 0) { tag->active = 0; } return 0; } #define AUTHENTICATE_LEGACY 0x0A #define AUTHENTICATE_ISO 0x1A #define AUTHENTICATE_AES 0xAA static int authenticate (MifareTag tag, uint8_t cmd, uint8_t key_no, MifareDESFireKey key) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); memset (MIFARE_DESFIRE (tag)->ivect, 0, MAX_CRYPTO_BLOCK_SIZE); MIFARE_DESFIRE (tag)->authenticated_key_no = NOT_YET_AUTHENTICATED; free (MIFARE_DESFIRE (tag)->session_key); MIFARE_DESFIRE (tag)->session_key = NULL; MIFARE_DESFIRE (tag)->authentication_scheme = (AUTHENTICATE_LEGACY == cmd) ? AS_LEGACY : AS_NEW; BUFFER_INIT (cmd1, 2); BUFFER_INIT (res, 17); BUFFER_APPEND (cmd1, cmd); BUFFER_APPEND (cmd1, key_no); DESFIRE_TRANSCEIVE (tag, cmd1, res); size_t key_length = __res_n - 1; uint8_t PICC_E_RndB[16]; memcpy (PICC_E_RndB, res, key_length); uint8_t PICC_RndB[16]; memcpy (PICC_RndB, PICC_E_RndB, key_length); mifare_cypher_blocks_chained (tag, key, MIFARE_DESFIRE (tag)->ivect, PICC_RndB, key_length, MCD_RECEIVE, MCO_DECYPHER); uint8_t PCD_RndA[16]; RAND_bytes (PCD_RndA, 16); uint8_t PCD_r_RndB[16]; memcpy (PCD_r_RndB, PICC_RndB, key_length); rol (PCD_r_RndB, key_length); uint8_t token[32]; memcpy (token, PCD_RndA, key_length); memcpy (token+key_length, PCD_r_RndB, key_length); mifare_cypher_blocks_chained (tag, key, MIFARE_DESFIRE (tag)->ivect, token, 2 * key_length, MCD_SEND, (AUTHENTICATE_LEGACY == cmd) ? MCO_DECYPHER : MCO_ENCYPHER); BUFFER_INIT (cmd2, 33); BUFFER_APPEND (cmd2, 0xAF); BUFFER_APPEND_BYTES (cmd2, token, 2*key_length); DESFIRE_TRANSCEIVE (tag, cmd2, res); uint8_t PICC_E_RndA_s[16]; memcpy (PICC_E_RndA_s, res, key_length); uint8_t PICC_RndA_s[16]; memcpy (PICC_RndA_s, PICC_E_RndA_s, key_length); mifare_cypher_blocks_chained (tag, key, MIFARE_DESFIRE (tag)->ivect, PICC_RndA_s, key_length, MCD_RECEIVE, MCO_DECYPHER); uint8_t PCD_RndA_s[key_length]; memcpy (PCD_RndA_s, PCD_RndA, key_length); rol (PCD_RndA_s, key_length); if (0 != memcmp (PCD_RndA_s, PICC_RndA_s, key_length)) { #ifdef WITH_DEBUG hexdump (PCD_RndA_s, key_length, "PCD ", 0); hexdump (PICC_RndA_s, key_length, "PICC ", 0); #endif return -1; } MIFARE_DESFIRE (tag)->authenticated_key_no = key_no; MIFARE_DESFIRE (tag)->session_key = mifare_desfire_session_key_new (PCD_RndA, PICC_RndB, key); memset (MIFARE_DESFIRE (tag)->ivect, 0, MAX_CRYPTO_BLOCK_SIZE); switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: break; case AS_NEW: cmac_generate_subkeys (MIFARE_DESFIRE (tag)->session_key); break; } return 0; } int mifare_desfire_authenticate (MifareTag tag, uint8_t key_no, MifareDESFireKey key) { switch (key->type) { case T_DES: case T_3DES: return authenticate (tag, AUTHENTICATE_LEGACY, key_no, key); break; case T_3K3DES: return authenticate (tag, AUTHENTICATE_ISO, key_no, key); break; case T_AES: return authenticate (tag, AUTHENTICATE_AES, key_no, key); break; } return -1; /* NOTREACHED */ } int mifare_desfire_authenticate_iso (MifareTag tag, uint8_t key_no, MifareDESFireKey key) { return authenticate (tag, AUTHENTICATE_ISO, key_no, key); } int mifare_desfire_authenticate_aes (MifareTag tag, uint8_t key_no, MifareDESFireKey key) { return authenticate (tag, AUTHENTICATE_AES, key_no, key); } int mifare_desfire_change_key_settings (MifareTag tag, uint8_t settings) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_AUTHENTICATED (tag); BUFFER_INIT (cmd, 9 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x54); BUFFER_APPEND (cmd, settings); char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 1, MDCM_ENCIPHERED | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t n = __res_n; p = mifare_cryto_postprocess_data (tag, res, &n, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY | MAC_COMMAND | MAC_VERIFY); if (!p) return errno = EINVAL, -1; return 0; } int mifare_desfire_get_key_settings (MifareTag tag, uint8_t *settings, uint8_t *max_keys) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 1); BUFFER_INIT (res, 3 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x45); char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 1, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t n = __res_n; p = mifare_cryto_postprocess_data (tag, res, &n, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; if (settings) *settings = p[0]; if (max_keys) *max_keys = p[1] & 0x0F; return 0; } int mifare_desfire_change_key (MifareTag tag, uint8_t key_no, MifareDESFireKey new_key, MifareDESFireKey old_key) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_AUTHENTICATED (tag); BUFFER_INIT (cmd, 42); BUFFER_INIT (res, 1 + CMAC_LENGTH); key_no &= 0x0F; /* * Because new crypto methods can be setup only at application creation, * changing the card master key to one of them require a key_no tweak. */ if (0x000000 == MIFARE_DESFIRE (tag)->selected_application) { switch (new_key->type) { case T_DES: case T_3DES: break; case T_3K3DES: key_no |= 0x40; break; case T_AES: key_no |= 0x80; break; } } BUFFER_APPEND (cmd, 0xC4); BUFFER_APPEND (cmd, key_no); int new_key_length; switch (new_key->type) { case T_DES: case T_3DES: case T_AES: new_key_length = 16; break; case T_3K3DES: new_key_length = 24; break; } memcpy (cmd + __cmd_n, new_key->data, new_key_length); if ((MIFARE_DESFIRE (tag)->authenticated_key_no & 0x0f) != (key_no & 0x0f)) { if (old_key) { for (int n = 0; n < new_key_length; n++) { cmd[__cmd_n + n] ^= old_key->data[n]; } } } __cmd_n += new_key_length; if (new_key->type == T_AES) cmd[__cmd_n++] = new_key->aes_version; if ((MIFARE_DESFIRE (tag)->authenticated_key_no & 0x0f) != (key_no & 0x0f)) { switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: iso14443a_crc_append (cmd + 2, __cmd_n - 2); __cmd_n += 2; iso14443a_crc (new_key->data, new_key_length, cmd + __cmd_n); __cmd_n += 2; break; case AS_NEW: desfire_crc32_append (cmd, __cmd_n); __cmd_n += 4; desfire_crc32 (new_key->data, new_key_length, cmd + __cmd_n); __cmd_n += 4; break; } } else { switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: iso14443a_crc_append (cmd + 2 , __cmd_n - 2); __cmd_n += 2; break; case AS_NEW: desfire_crc32_append (cmd, __cmd_n); __cmd_n += 4; break; } } uint8_t * p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, MDCM_ENCIPHERED | ENC_COMMAND | NO_CRC); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; /* * If we changed the current authenticated key, we are not authenticated * anymore. */ if (key_no == MIFARE_DESFIRE (tag)->authenticated_key_no) { free (MIFARE_DESFIRE (tag)->session_key); MIFARE_DESFIRE (tag)->session_key = NULL; } return 0; } /* * Retrieve version information for a given key. */ int mifare_desfire_get_key_version (MifareTag tag, uint8_t key_no, uint8_t *version) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_NOT_NULL (version); BUFFER_INIT (cmd, 2); BUFFER_APPEND (cmd, 0x64); BUFFER_APPEND (cmd, key_no); BUFFER_INIT (res, 2 + CMAC_LENGTH); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY | MAC_VERIFY); if (!p) return errno = EINVAL, -1; *version = p[0]; return 0; } static int create_application (MifareTag tag, MifareDESFireAID aid, uint8_t settings1, uint8_t settings2, int want_iso_application, int want_iso_file_identifiers, uint16_t iso_file_id, uint8_t *iso_file_name, size_t iso_file_name_len) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 22); BUFFER_INIT (res, 1 + CMAC_LENGTH); if (want_iso_file_identifiers) settings2 |= 0x20; BUFFER_APPEND (cmd, 0xCA); BUFFER_APPEND_LE (cmd, aid->data, sizeof (aid->data), sizeof (aid->data)); BUFFER_APPEND (cmd, settings1); BUFFER_APPEND (cmd, settings2); if (want_iso_application) BUFFER_APPEND_LE (cmd, iso_file_id, sizeof (iso_file_id), sizeof (iso_file_id)); if (iso_file_name_len) BUFFER_APPEND_BYTES (cmd, iso_file_name, iso_file_name_len); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY | MAC_VERIFY); if (!p) return errno = EINVAL, -1; return 0; } int mifare_desfire_create_application (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no) { return create_application (tag, aid, settings, key_no, 0, 0, 0, NULL, 0); } int mifare_desfire_create_application_iso (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no, int want_iso_file_identifiers, uint16_t iso_file_id, uint8_t *iso_file_name, size_t iso_file_name_len) { return create_application (tag, aid, settings, key_no, 1, want_iso_file_identifiers, iso_file_id, iso_file_name, iso_file_name_len); } int mifare_desfire_create_application_3k3des (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no) { return create_application (tag, aid, settings, APPLICATION_CRYPTO_3K3DES | key_no, 0, 0, 0, NULL, 0); } int mifare_desfire_create_application_3k3des_iso (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no, int want_iso_file_identifiers, uint16_t iso_file_id, uint8_t *iso_file_name, size_t iso_file_name_len) { return create_application (tag, aid, settings, APPLICATION_CRYPTO_3K3DES | key_no, 1, want_iso_file_identifiers, iso_file_id, iso_file_name, iso_file_name_len); } int mifare_desfire_create_application_aes (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no) { return create_application (tag, aid, settings, APPLICATION_CRYPTO_AES | key_no, 0, 0, 0, NULL, 0); } int mifare_desfire_create_application_aes_iso (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no, int want_iso_file_identifiers, uint16_t iso_file_id, uint8_t *iso_file_name, size_t iso_file_name_len) { return create_application (tag, aid, settings, APPLICATION_CRYPTO_AES | key_no, 1, want_iso_file_identifiers, iso_file_id, iso_file_name, iso_file_name_len); } int mifare_desfire_delete_application (MifareTag tag, MifareDESFireAID aid) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 4 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xDA); BUFFER_APPEND_LE (cmd, aid->data, sizeof (aid->data), sizeof (aid->data)); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; /* * If we have deleted the current application, we are not authenticated * anymore. */ if (MIFARE_DESFIRE (tag)->selected_application == (uint32_t)(aid->data[0] | aid->data[1] << 8 | aid->data[2] << 16)) { free (MIFARE_DESFIRE (tag)->session_key); MIFARE_DESFIRE (tag)->session_key = NULL; MIFARE_DESFIRE (tag)->selected_application = 0x000000; } return 0; } int mifare_desfire_get_application_ids (MifareTag tag, MifareDESFireAID *aids[], size_t *count) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 1); BUFFER_INIT (res, MAX_RAPDU_SIZE); BUFFER_APPEND (cmd, 0x6A); uint8_t buffer[3*MAX_APPLICATION_COUNT + CMAC_LENGTH + 1]; *count = 0; uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); // FIXME This code needs refactoring! memcpy (buffer, res, __res_n); if (res[__res_n-1] == 0xAF) { off_t offset = __res_n - 1; p[0] = 0xAF; DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); memcpy ((uint8_t *)buffer + offset, res, __res_n); __res_n += offset; } ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, buffer, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY | MAC_VERIFY); if (!p) return errno = EINVAL, -1; *count = (sn - 1)/3; *aids = malloc (sn - 1); if (!(*aids = malloc ((*count + 1) * sizeof (MifareDESFireAID)))) return -1; for (size_t i = 0; i < *count; i++) { if (!((*aids)[i] = memdup (p + 3 * i, 3))) { while (i--) { free ((*aids)[i]); } free (aids); return -1; } } (*aids)[*count] = NULL; return 0; } int mifare_desfire_get_df_names (MifareTag tag, MifareDESFireDF *dfs[], size_t *count) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); *count = 0; *dfs = NULL; BUFFER_INIT (cmd, 1); BUFFER_INIT (res, 22 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x6D); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); do { DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); if (__res_n > 1) { MifareDESFireDF *new_dfs; if ((new_dfs = realloc (*dfs, sizeof (*new_dfs) * (*count + 1)))) { new_dfs[*count].aid = le24toh (res); new_dfs[*count].fid = le16toh (*(uint16_t *)(res + 3)); memcpy (new_dfs[*count].df_name, res + 5, __res_n - 6); new_dfs[*count].df_name_len = __res_n - 6; *dfs = new_dfs; *count += 1; } } p[0] = 0XAF; } while (res[__res_n-1] == 0xAF); return 0; } void mifare_desfire_free_application_ids (MifareDESFireAID aids[]) { for (int i = 0; aids[i]; i++) free (aids[i]); free (aids); } /* * Select the application specified by aid for further operation. If aid is * NULL, the master application is selected (equivalent to aid = 0x00000). */ int mifare_desfire_select_application (MifareTag tag, MifareDESFireAID aid) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); struct mifare_desfire_aid null_aid = { .data = { 0x00, 0x00, 0x00 } }; if (!aid) { aid = &null_aid; } BUFFER_INIT (cmd, 4 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x5A); BUFFER_APPEND_LE (cmd, aid->data, sizeof (aid->data), sizeof (aid->data)); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND); if (!p) return errno = EINVAL, -1; for (int n = 0; n < MAX_FILE_COUNT; n++) cached_file_settings_current[n] = false; free (MIFARE_DESFIRE (tag)->session_key); MIFARE_DESFIRE (tag)->session_key = NULL; MIFARE_DESFIRE (tag)->selected_application = aid->data[0] | aid->data[1] << 8 | aid->data[2] << 16; return 0; } int mifare_desfire_format_picc (MifareTag tag) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_AUTHENTICATED (tag); BUFFER_INIT (cmd, 1 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xFC); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; free (MIFARE_DESFIRE (tag)->session_key); MIFARE_DESFIRE (tag)->session_key = NULL; MIFARE_DESFIRE (tag)->selected_application = 0x000000; return 0; } /* * Retrieve version information form the PICC. */ int mifare_desfire_get_version (MifareTag tag, struct mifare_desfire_version_info *version_info) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_NOT_NULL (version_info); BUFFER_INIT (cmd, 1); BUFFER_INIT (res, 15 + CMAC_LENGTH); /* 8, 8, then 15 byte results */ char buffer[28 + CMAC_LENGTH + 1]; BUFFER_APPEND (cmd, 0x60); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); memcpy (&(version_info->hardware), res, 7); memcpy (buffer, res, 7); p[0] = 0xAF; DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); memcpy (&(version_info->software), res, 7); memcpy (buffer + 7, res, 7); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); memcpy (&(version_info->uid), res, 14); memcpy (buffer + 14, res, __res_n); ssize_t sn = 28 + CMAC_LENGTH + 1; p = mifare_cryto_postprocess_data (tag, buffer, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; return 0; } int mifare_desfire_free_mem (MifareTag tag, uint32_t *size) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_NOT_NULL (size); BUFFER_INIT (cmd, 1); BUFFER_INIT (res, 4 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x6E); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; *size = p[0] | (p[1] << 8) | (p[2] << 16); return 0; } int mifare_desfire_set_configuration (MifareTag tag, bool disable_format, bool enable_random_uid) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 10); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x5C); BUFFER_APPEND (cmd, 0x00); BUFFER_APPEND (cmd, (enable_random_uid ? 0x02 : 0x00) | (disable_format ? 0x01 : 0x00)); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, MDCM_ENCIPHERED | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; return 0; } int mifare_desfire_set_default_key (MifareTag tag, MifareDESFireKey key) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 34); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x5C); BUFFER_APPEND (cmd, 0x01); size_t key_data_length; switch (key->type) { case T_DES: case T_3DES: case T_AES: key_data_length = 16; break; case T_3K3DES: key_data_length = 24; break; } BUFFER_APPEND_BYTES (cmd, key->data, key_data_length); while (__cmd_n < 26) BUFFER_APPEND (cmd, 0x00); BUFFER_APPEND (cmd, mifare_desfire_key_get_version (key)); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, MDCM_ENCIPHERED | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; return 0; } int mifare_desfire_set_ats (MifareTag tag, uint8_t *ats) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 34); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x5C); BUFFER_APPEND (cmd, 0x02); BUFFER_APPEND_BYTES (cmd, ats, *ats); switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: iso14443a_crc_append (cmd + 2 , __cmd_n - 2); __cmd_n += 2; break; case AS_NEW: desfire_crc32_append (cmd, __cmd_n); __cmd_n += 4; break; } BUFFER_APPEND (cmd, 0x80); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, MDCM_ENCIPHERED | NO_CRC | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; return 0; } int mifare_desfire_get_card_uid (MifareTag tag, char **uid) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_NOT_NULL (uid); BUFFER_INIT (cmd, 1); BUFFER_INIT (res, 17 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x51); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 1, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_ENCIPHERED); if (!p) return errno = EINVAL, -1; if (!(*uid = malloc (2*7+1))) { return -1; } sprintf (*uid, "%02x%02x%02x%02x%02x%02x%02x", p[0], p[1], p[2], p[3], p[4], p[5], p[6]); return 0; } /* Application level commands */ int mifare_desfire_get_file_ids (MifareTag tag, uint8_t *files[], size_t *count) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 1 + CMAC_LENGTH); BUFFER_INIT (res, 16 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x6F); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; *count = sn - 1; if (!(*files = malloc (*count))) { errno = ENOMEM; return -1; } memcpy (*files, res, *count); return 0; } int mifare_desfire_get_iso_file_ids (MifareTag tag, uint16_t *files[], size_t *count) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 1); BUFFER_INIT (res, 2*27 + 1); BUFFER_APPEND (cmd, 0x61); uint8_t *data; if (!(data = malloc ((27 + 5) * 2 + 8))) return -1; off_t offset = 0; uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); do { DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); memcpy (data + offset, res, __res_n - 1); offset += __res_n - 1; p[0] = 0XAF; } while (res[__res_n-1] == 0xAF); ssize_t sn = offset; p = mifare_cryto_postprocess_data (tag, data, &sn, MDCM_PLAIN | CMAC_COMMAND); if (!p) return errno = EINVAL, -1; *count = sn / 2; *files = malloc (sizeof (**files) * *count); if (!*files) return -1; for (size_t i = 0; i < *count; i++) { (*files)[i] = le16toh (*(uint16_t *)(p + (2*i))); } return 0; } int mifare_desfire_get_file_settings (MifareTag tag, uint8_t file_no, struct mifare_desfire_file_settings *settings) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); if (cached_file_settings_current[file_no]) { *settings = cached_file_settings[file_no]; return 0; } BUFFER_INIT (cmd, 2 + CMAC_LENGTH); BUFFER_INIT (res, 18 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xF5); BUFFER_APPEND (cmd, file_no); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; struct mifare_desfire_raw_file_settings raw_settings; memcpy (&raw_settings, p, sn - 1); settings->file_type = raw_settings.file_type; settings->communication_settings = raw_settings.communication_settings; settings->access_rights = le16toh (raw_settings.access_rights); switch (settings->file_type) { case MDFT_STANDARD_DATA_FILE: case MDFT_BACKUP_DATA_FILE: settings->settings.standard_file.file_size = le24toh (raw_settings.settings.standard_file.file_size); break; case MDFT_VALUE_FILE_WITH_BACKUP: settings->settings.value_file.lower_limit = le32toh (raw_settings.settings.value_file.lower_limit); settings->settings.value_file.upper_limit = le32toh (raw_settings.settings.value_file.upper_limit); settings->settings.value_file.limited_credit_value = le32toh (raw_settings.settings.value_file.limited_credit_value); settings->settings.value_file.limited_credit_enabled = raw_settings.settings.value_file.limited_credit_enabled; break; case MDFT_LINEAR_RECORD_FILE_WITH_BACKUP: case MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP: settings->settings.linear_record_file.record_size = le24toh (raw_settings.settings.linear_record_file.record_size); settings->settings.linear_record_file.max_number_of_records = le24toh (raw_settings.settings.linear_record_file.max_number_of_records); settings->settings.linear_record_file.current_number_of_records = le24toh (raw_settings.settings.linear_record_file.current_number_of_records); break; } cached_file_settings[file_no] = *settings; cached_file_settings_current[file_no] = true; return 0; } int mifare_desfire_change_file_settings (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); struct mifare_desfire_file_settings settings; int res = mifare_desfire_get_file_settings (tag, file_no, &settings); if (res < 0) return res; cached_file_settings_current[file_no] = false; if (MDAR_CHANGE_AR(settings.access_rights) == MDAR_FREE) { BUFFER_INIT (cmd, 5 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x5F); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND (cmd, communication_settings); BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; } else { BUFFER_INIT (cmd, 10); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x5F); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND (cmd, communication_settings); BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, MDCM_ENCIPHERED | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; } return 0; } static int create_file1 (MifareTag tag, uint8_t command, uint8_t file_no, int has_iso_file_id, uint16_t iso_file_id, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 10 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, command); BUFFER_APPEND (cmd, file_no); if (has_iso_file_id) BUFFER_APPEND_LE (cmd, iso_file_id, sizeof (iso_file_id), sizeof (iso_file_id)); BUFFER_APPEND (cmd, communication_settings); BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); BUFFER_APPEND_LE (cmd, file_size, 3, sizeof (uint32_t)); char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; cached_file_settings_current[file_no] = false; return 0; } int mifare_desfire_create_std_data_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size) { return create_file1 (tag, 0xCD, file_no, 0, 0x0000, communication_settings, access_rights, file_size); } int mifare_desfire_create_std_data_file_iso (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size, uint16_t iso_file_id) { return create_file1 (tag, 0xCD, file_no, 1, iso_file_id, communication_settings, access_rights, file_size); } int mifare_desfire_create_backup_data_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size) { return create_file1 (tag, 0xCB, file_no, 0, 0x0000, communication_settings, access_rights, file_size); } int mifare_desfire_create_backup_data_file_iso (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size, uint16_t iso_file_id) { return create_file1 (tag, 0xCB, file_no, 1, iso_file_id, communication_settings, access_rights, file_size); } int mifare_desfire_create_value_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, int32_t lower_limit, int32_t upper_limit, int32_t value, uint8_t limited_credit_enable) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 18 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xCC); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND (cmd, communication_settings); BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); BUFFER_APPEND_LE (cmd, lower_limit, 4, sizeof (int32_t)); BUFFER_APPEND_LE (cmd, upper_limit, 4, sizeof (int32_t)); BUFFER_APPEND_LE (cmd, value, 4, sizeof (int32_t)); BUFFER_APPEND (cmd, limited_credit_enable); char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; cached_file_settings_current[file_no] = false; return 0; } static int create_file2 (MifareTag tag, uint8_t command, uint8_t file_no, int has_iso_file_id, uint16_t iso_file_id, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 11 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, command); BUFFER_APPEND (cmd, file_no); if (has_iso_file_id) BUFFER_APPEND_LE (cmd, iso_file_id, sizeof (iso_file_id), sizeof (iso_file_id)); BUFFER_APPEND (cmd, communication_settings); BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); BUFFER_APPEND_LE (cmd, record_size, 3, sizeof (uint32_t)); BUFFER_APPEND_LE (cmd, max_number_of_records, 3, sizeof (uint32_t)); char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; cached_file_settings_current[file_no] = false; return 0; } int mifare_desfire_create_linear_record_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records) { return create_file2 (tag, 0xC1, file_no, 0, 0x0000, communication_settings, access_rights, record_size, max_number_of_records); } int mifare_desfire_create_linear_record_file_iso (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records, uint16_t iso_file_id) { return create_file2 (tag, 0xC1, file_no, 1, iso_file_id, communication_settings, access_rights, record_size, max_number_of_records); } int mifare_desfire_create_cyclic_record_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records) { return create_file2 (tag, 0xC0, file_no, 0, 0x000, communication_settings, access_rights, record_size, max_number_of_records); } int mifare_desfire_create_cyclic_record_file_iso (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records, uint16_t iso_file_id) { return create_file2 (tag, 0xC0, file_no, 1, iso_file_id, communication_settings, access_rights, record_size, max_number_of_records); } int mifare_desfire_delete_file (MifareTag tag, uint8_t file_no) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 2 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xDF); BUFFER_APPEND (cmd, file_no); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; return 0; } /* * Data manipulation commands. */ static ssize_t read_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, void *data, int cs) { size_t bytes_received = 0; ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); BUFFER_INIT (cmd, 8); BUFFER_INIT (res, MAX_RAPDU_SIZE); BUFFER_APPEND (cmd, command); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND_LE (cmd, offset, 3, sizeof (off_t)); BUFFER_APPEND_LE (cmd, length, 3, sizeof (size_t)); uint8_t ocs = cs; if ((MIFARE_DESFIRE (tag)->session_key) && (cs | MDCM_MACED)) { switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: break; case AS_NEW: cs = MDCM_PLAIN; break; } } uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 8, MDCM_PLAIN | CMAC_COMMAND); cs = ocs; /* * FIXME: This is bogus: the user has to provide a data buffer with enougth * room to store CRC + padding or MAC. If the user wants to read 1 byte, * there is no reason to provide a 16 bytes buffer. */ do { DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); size_t frame_bytes = BUFFER_SIZE (res) - 1; memcpy ((uint8_t *)data + bytes_received, res, frame_bytes); bytes_received += frame_bytes; p[0] = 0xAF; __cmd_n = 1; } while (0xAF == res[__res_n-1]); ((uint8_t *)data)[bytes_received++] = 0x00; ssize_t sr = bytes_received; p = mifare_cryto_postprocess_data (tag, data, &sr, cs | CMAC_COMMAND | CMAC_VERIFY | MAC_VERIFY); if (!p) return errno = EINVAL, -1; return (sr <= 0) ? sr : sr - 1; } ssize_t mifare_desfire_read_data (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data) { return mifare_desfire_read_data_ex (tag, file_no, offset, length, data, madame_soleil_get_read_communication_settings (tag, file_no)); } ssize_t mifare_desfire_read_data_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs) { return read_data (tag, 0xBD, file_no, offset, length, data, cs); } static ssize_t write_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, const void *data, int cs) { size_t bytes_left; size_t bytes_send = 0; ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); BUFFER_INIT (cmd, 8 + length + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, command); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND_LE (cmd, offset, 3, sizeof (off_t)); BUFFER_APPEND_LE (cmd, length, 3, sizeof (size_t)); BUFFER_APPEND_BYTES (cmd, data, length); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 8, cs | MAC_COMMAND | CMAC_COMMAND | ENC_COMMAND); size_t overhead_size = __cmd_n - length; // (CRC | padding) + headers BUFFER_INIT(d, MAX_CAPDU_SIZE); bytes_left = __d_size; while (bytes_send < __cmd_n) { size_t frame_bytes = MIN(bytes_left, __cmd_n - bytes_send); BUFFER_APPEND_BYTES (d, p + bytes_send, frame_bytes); DESFIRE_TRANSCEIVE (tag, d, res); bytes_send += frame_bytes; if (0x00 == res[__res_n-1]) break; // PICC returned 0xAF and expects more data BUFFER_CLEAR (d); bytes_left = __d_size; BUFFER_APPEND (d, 0xAF); bytes_left--; } ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; if (0x00 == p[__res_n-1]) { // Remove header length bytes_send -= overhead_size; } else { // 0xAF (additionnal Frame) failure can happen here (wrong crypto method). MIFARE_DESFIRE (tag)->last_picc_error = p[__res_n-1]; bytes_send = -1; } cached_file_settings_current[file_no] = false; return bytes_send; } ssize_t mifare_desfire_write_data (MifareTag tag, uint8_t file_no, off_t offset, size_t length, const void *data) { return mifare_desfire_write_data_ex (tag, file_no, offset, length, data, madame_soleil_get_write_communication_settings (tag, file_no)); } ssize_t mifare_desfire_write_data_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, const void *data, int cs) { return write_data (tag, 0x3D, file_no, offset, length, data, cs); } int mifare_desfire_get_value (MifareTag tag, uint8_t file_no, int32_t *value) { return mifare_desfire_get_value_ex (tag, file_no, value, madame_soleil_get_read_communication_settings (tag, file_no)); } int mifare_desfire_get_value_ex (MifareTag tag, uint8_t file_no, int32_t *value, int cs) { if (!value) return errno = EINVAL, -1; ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); BUFFER_INIT (cmd, 2 + CMAC_LENGTH); BUFFER_INIT (res, 9 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x6C); BUFFER_APPEND (cmd, file_no); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, cs | CMAC_COMMAND | CMAC_VERIFY | MAC_VERIFY); if (!p) return errno = EINVAL, -1; *value = le32toh (*(int32_t *)(p)); return 0; } int mifare_desfire_credit (MifareTag tag, uint8_t file_no, int32_t amount) { return mifare_desfire_credit_ex (tag, file_no, amount, madame_soleil_get_write_communication_settings (tag, file_no)); } int mifare_desfire_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); BUFFER_INIT (cmd, 10 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x0C); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND_LE (cmd, amount, 4, sizeof (int32_t)); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, cs | MAC_COMMAND | CMAC_COMMAND | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; cached_file_settings_current[file_no] = false; return 0; } int mifare_desfire_debit (MifareTag tag, uint8_t file_no, int32_t amount) { return mifare_desfire_debit_ex (tag, file_no, amount, madame_soleil_get_write_communication_settings (tag, file_no)); } int mifare_desfire_debit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); BUFFER_INIT (cmd, 10 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xDC); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND_LE (cmd, amount, 4, sizeof (int32_t)); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, cs | MAC_COMMAND | CMAC_COMMAND | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; cached_file_settings_current[file_no] = false; return 0; } int mifare_desfire_limited_credit (MifareTag tag, uint8_t file_no, int32_t amount) { return mifare_desfire_limited_credit_ex (tag, file_no, amount, madame_soleil_get_write_communication_settings (tag, file_no)); } int mifare_desfire_limited_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); BUFFER_INIT (cmd, 10 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x1C); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND_LE (cmd, amount, 4, sizeof (int32_t)); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, cs | MAC_COMMAND | CMAC_COMMAND | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; cached_file_settings_current[file_no] = false; return 0; } ssize_t mifare_desfire_write_record (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data) { return mifare_desfire_write_record_ex (tag, file_no, offset, length, data, madame_soleil_get_write_communication_settings (tag, file_no)); } ssize_t mifare_desfire_write_record_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs) { return write_data (tag, 0x3B, file_no, offset, length, data, cs); } ssize_t mifare_desfire_read_records (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data) { return mifare_desfire_read_records_ex (tag, file_no, offset, length, data, madame_soleil_get_read_communication_settings (tag, file_no)); } ssize_t mifare_desfire_read_records_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs) { return read_data (tag, 0xBB, file_no, offset, length, data, cs); } int mifare_desfire_clear_record_file (MifareTag tag, uint8_t file_no) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 2 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xEB); BUFFER_APPEND (cmd, file_no); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; cached_file_settings_current[file_no] = false; return 0; } int mifare_desfire_commit_transaction (MifareTag tag) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 1 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xC7); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; return 0; } int mifare_desfire_abort_transaction (MifareTag tag) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 1 + CMAC_LENGTH); BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xA7); uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); ssize_t sn = __res_n; p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (!p) return errno = EINVAL, -1; return 0; } libfreefare-0.4.0/libfreefare/mifare_desfire_aid.3000066400000000000000000000073351225640673100221120ustar00rootroot00000000000000.\" Copyright (C) 2010 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id: mifare_desfire.3 495 2010-07-26 21:48:18Z rtartiere@il4p.fr $ .\" .Dd September 3, 2010 .Dt MIFARE_DESFIRE_AID 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm mifare_desfire_aid_new , .Nm mifare_desfire_aid_new_with_mad_aid , .Nm mifare_desfire_aid_get_aid .Nd Mifare DESFire AID Manipulation Functions .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft MifareDESFireAID .Fn mifare_desfire_aid_new "uint32_t aid" .Ft MifareDESFireAID .Fn mifare_desfire_aid_new_with_mad_aid "MadAid mad_aid" "uint8_t n" .Ft uint32_t .Fn mifare_desfire_aid_get_aid "MifareDESFireAID aid" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION The .Fn mifare_desfire_aid_* functions allows management of Mifare DESFire AIDs. The .Fn mifare_desfire_aid_new function returns a new Mifare DESFire AID of value .Vt aid . .Pp The .Fn mifare_desfire_aid_new_with_mad_aid functions returns a new Mifare DESFire AID using a Mifare Classic AID .Vt mad_aid and .Vt n as the last nibble of the new AID. .Pp Both .Fn mifare_desfire_aid_new and .Fn mifare_desfire_aid_new_with_mad_aid allocates memory that should be reclaimed using .Xr free 3 . .Pp The .Fn mifare_desfire_aid_get_aid function returns the .Vt aid of the provided Mifare DESFire AID. .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES The allocation functions returns the allocated AID on success, .Va NULL otherwise. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr freefare 3 , .Xr mifare_classic 3 , .Xr free 3 .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@il4p.org libfreefare-0.4.0/libfreefare/mifare_desfire_aid.c000066400000000000000000000045031225640673100221640ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ /* * This implementation was written based on information provided by the * following document: * * Mifare DESFire Specification by LASSeO * Version 1.0 - 29'th September 2009 * http://www.scnf.org.uk/smartstore/LASSeO%20docs/DESFIRE%20Specification%20V1%200.pdf */ #include "config.h" #if defined(HAVE_SYS_TYPES_H) # include #endif #if defined(HAVE_SYS_ENDIAN_H) # include #endif #if defined(HAVE_ENDIAN_H) # include #endif #if defined(HAVE_COREFOUNDATION_COREFOUNDATION_H) # include #endif #if defined(HAVE_BYTESWAP_H) # include #endif #include #include #include #include #include "freefare_internal.h" // Theorically, it should be an uint24_t ... MifareDESFireAID mifare_desfire_aid_new (uint32_t aid) { if (aid > 0x00ffffff) return errno = EINVAL, NULL; MifareDESFireAID res; uint32_t aid_le = htole32 (aid); if ((res = malloc (sizeof (*res)))) { memcpy(res->data, ((uint8_t*)&aid_le), 3); } return res; } // This function ease the MifareDESFireAID creation using a Mifare Classic AID (see MIFARE Application Directory document - section 3.10 MAD and MIFARE DESFire) MifareDESFireAID mifare_desfire_aid_new_with_mad_aid (MadAid mad_aid, uint8_t n) { if (n > 0x0f) return errno = EINVAL, NULL; return mifare_desfire_aid_new (0xf00000 | (mad_aid.function_cluster_code << 12) | (mad_aid.application_code << 4) | n); } uint32_t mifare_desfire_aid_get_aid (MifareDESFireAID aid) { return aid->data[0] | (aid->data[1] << 8) | (aid->data[2] << 16); } libfreefare-0.4.0/libfreefare/mifare_desfire_crypto.c000066400000000000000000000504541225640673100227550ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ /* * This implementation was written based on information provided by the * following documents: * * NIST Special Publication 800-38B * Recommendation for Block Cipher Modes of Operation: The CMAC Mode for Authentication * May 2005 */ #include "config.h" #if defined(HAVE_SYS_TYPES_H) # include #endif #if defined(HAVE_SYS_ENDIAN_H) # include #endif #if defined(HAVE_ENDIAN_H) # include #endif #if defined(HAVE_COREFOUNDATION_COREFOUNDATION_H) # include #endif #if defined(HAVE_BYTESWAP_H) # include #endif #if defined(HAVE_SYS_TYPES_H) # include #endif #include #include #include #include #include #ifdef WITH_DEBUG # include #endif #include #include "freefare_internal.h" #define MAC_LENGTH 4 #define CMAC_LENGTH 8 static void xor (const uint8_t *ivect, uint8_t *data, const size_t len); static void desfire_crc32_byte (uint32_t *crc, const uint8_t value); static size_t key_macing_length (MifareDESFireKey key); static void xor (const uint8_t *ivect, uint8_t *data, const size_t len) { for (size_t i = 0; i < len; i++) { data[i] ^= ivect[i]; } } void rol (uint8_t *data, const size_t len) { uint8_t first = data[0]; for (size_t i = 0; i < len-1; i++) { data[i] = data[i+1]; } data[len-1] = first; } void lsl (uint8_t *data, size_t len) { for (size_t n = 0; n < len - 1; n++) { data[n] = (data[n] << 1) | (data[n+1] >> 7); } data[len - 1] <<= 1; } void cmac_generate_subkeys (MifareDESFireKey key) { int kbs = key_block_size (key); const uint8_t R = (kbs == 8) ? 0x1B : 0x87; uint8_t l[kbs]; memset (l, 0, kbs); uint8_t ivect[kbs]; memset (ivect, 0, kbs); mifare_cypher_blocks_chained (NULL, key, ivect, l, kbs, MCD_RECEIVE, MCO_ENCYPHER); bool xor = false; // Used to compute CMAC on complete blocks memcpy (key->cmac_sk1, l, kbs); xor = l[0] & 0x80; lsl (key->cmac_sk1, kbs); if (xor) key->cmac_sk1[kbs-1] ^= R; // Used to compute CMAC on the last block if non-complete memcpy (key->cmac_sk2, key->cmac_sk1, kbs); xor = key->cmac_sk1[0] & 0x80; lsl (key->cmac_sk2, kbs); if (xor) key->cmac_sk2[kbs-1] ^= R; } void cmac (const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac) { int kbs = key_block_size (key); uint8_t *buffer = malloc (padded_data_length (len, kbs)); if (!buffer) abort(); memcpy (buffer, data, len); if ((!len) || (len % kbs)) { buffer[len++] = 0x80; while (len % kbs) { buffer[len++] = 0x00; } xor (key->cmac_sk2, buffer + len - kbs, kbs); } else { xor (key->cmac_sk1, buffer + len - kbs, kbs); } mifare_cypher_blocks_chained (NULL, key, ivect, buffer, len, MCD_SEND, MCO_ENCYPHER); memcpy (cmac, ivect, kbs); free (buffer); } #define CRC32_PRESET 0xFFFFFFFF static void desfire_crc32_byte (uint32_t *crc, const uint8_t value) { /* x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 */ const uint32_t poly = 0xEDB88320; *crc ^= value; for (int current_bit = 7; current_bit >= 0; current_bit--) { int bit_out = (*crc) & 0x00000001; *crc >>= 1; if (bit_out) *crc ^= poly; } } void desfire_crc32 (const uint8_t *data, const size_t len, uint8_t *crc) { uint32_t desfire_crc = CRC32_PRESET; for (size_t i = 0; i < len; i++) { desfire_crc32_byte (&desfire_crc, data[i]); } *((uint32_t *)(crc)) = htole32 (desfire_crc); } void desfire_crc32_append (uint8_t *data, const size_t len) { desfire_crc32 (data, len, data + len); } size_t key_block_size (const MifareDESFireKey key) { size_t block_size; switch (key->type) { case T_DES: case T_3DES: case T_3K3DES: block_size = 8; break; case T_AES: block_size = 16; break; } return block_size; } /* * Size of MACing produced with the key. */ static size_t key_macing_length (const MifareDESFireKey key) { size_t mac_length; switch (key->type) { case T_DES: case T_3DES: mac_length = MAC_LENGTH; break; case T_3K3DES: case T_AES: mac_length = CMAC_LENGTH; break; } return mac_length; } /* * Size required to store nbytes of data in a buffer of size n*block_size. */ size_t padded_data_length (const size_t nbytes, const size_t block_size) { if ((!nbytes) || (nbytes % block_size)) return ((nbytes / block_size) + 1) * block_size; else return nbytes; } /* * Buffer size required to MAC nbytes of data */ size_t maced_data_length (const MifareDESFireKey key, const size_t nbytes) { return nbytes + key_macing_length (key); } /* * Buffer size required to encipher nbytes of data and a two bytes CRC. */ size_t enciphered_data_length (const MifareTag tag, const size_t nbytes, int communication_settings) { size_t crc_length = 0; if (!(communication_settings & NO_CRC)) { switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: crc_length = 2; break; case AS_NEW: crc_length = 4; break; } } size_t block_size = key_block_size (MIFARE_DESFIRE (tag)->session_key); return padded_data_length (nbytes + crc_length, block_size); } /* * Ensure that tag's crypto buffer is large enough to store nbytes of data. */ void * assert_crypto_buffer_size (MifareTag tag, size_t nbytes) { void *res = MIFARE_DESFIRE (tag)->crypto_buffer; if (MIFARE_DESFIRE (tag)->crypto_buffer_size < nbytes) { if ((res = realloc (MIFARE_DESFIRE (tag)->crypto_buffer, nbytes))) { MIFARE_DESFIRE (tag)->crypto_buffer = res; MIFARE_DESFIRE (tag)->crypto_buffer_size = nbytes; } } return res; } void * mifare_cryto_preprocess_data (MifareTag tag, void *data, size_t *nbytes, off_t offset, int communication_settings) { uint8_t *res = data; uint8_t mac[4]; size_t edl, mdl; bool append_mac = true; MifareDESFireKey key = MIFARE_DESFIRE (tag)->session_key; if (!key) return data; switch (communication_settings & MDCM_MASK) { case MDCM_PLAIN: if (AS_LEGACY == MIFARE_DESFIRE (tag)->authentication_scheme) break; /* * When using new authentication methods, PLAIN data transmission from * the PICC to the PCD are CMACed, so we have to maintain the * cryptographic initialisation vector up-to-date to check data * integrity later. * * The only difference with CMACed data transmission is that the CMAC * is not apended to the data send by the PCD to the PICC. */ append_mac = false; /* pass through */ case MDCM_MACED: switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: if (!(communication_settings & MAC_COMMAND)) break; /* pass through */ edl = padded_data_length (*nbytes - offset, key_block_size (MIFARE_DESFIRE (tag)->session_key)) + offset; if (!(res = assert_crypto_buffer_size (tag, edl))) abort(); // Fill in the crypto buffer with data ... memcpy (res, data, *nbytes); // ... and 0 padding memset (res + *nbytes, 0, edl - *nbytes); mifare_cypher_blocks_chained (tag, NULL, NULL, res + offset, edl - offset, MCD_SEND, MCO_ENCYPHER); memcpy (mac, res + edl - 8, 4); // Copy again provided data (was overwritten by mifare_cypher_blocks_chained) memcpy (res, data, *nbytes); if (!(communication_settings & MAC_COMMAND)) break; // Append MAC mdl = maced_data_length (MIFARE_DESFIRE (tag)->session_key, *nbytes - offset) + offset; if (!(res = assert_crypto_buffer_size (tag, mdl))) abort(); memcpy (res + *nbytes, mac, 4); *nbytes += 4; break; case AS_NEW: if (!(communication_settings & CMAC_COMMAND)) break; cmac (key, MIFARE_DESFIRE (tag)->ivect, res, *nbytes, MIFARE_DESFIRE (tag)->cmac); if (append_mac) { mdl = maced_data_length (key, *nbytes); if (!(res = assert_crypto_buffer_size (tag, mdl))) abort(); memcpy (res, data, *nbytes); memcpy (res + *nbytes, MIFARE_DESFIRE (tag)->cmac, CMAC_LENGTH); *nbytes += CMAC_LENGTH; } break; } break; case MDCM_ENCIPHERED: /* |<-------------- data -------------->| * |<--- offset -->| | * +---------------+--------------------+-----+---------+ * | CMD + HEADERS | DATA TO BE SECURED | CRC | PADDING | * +---------------+--------------------+-----+---------+ ---------------- * | |<~~~~v~~~~~~~~~~~~~>| ^ | | (DES / 3DES) * | | `---- crc16() ----' | | * | | | ^ | | ----- *or* ----- * |<~~~~~~~~~~~~~~~~~~~~v~~~~~~~~~~~~~>| ^ | | (3K3DES / AES) * | `---- crc32() ----' | | * | | ---- *then* ---- * |<---------------------------------->| * encypher()/decypher() */ if (!(communication_settings & ENC_COMMAND)) break; edl = enciphered_data_length (tag, *nbytes - offset, communication_settings) + offset; if (!(res = assert_crypto_buffer_size (tag, edl))) abort(); // Fill in the crypto buffer with data ... memcpy (res, data, *nbytes); if (!(communication_settings & NO_CRC)) { // ... CRC ... switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: iso14443a_crc_append (res + offset, *nbytes - offset); *nbytes += 2; break; case AS_NEW: desfire_crc32_append (res, *nbytes); *nbytes += 4; break; } } // ... and padding memset (res + *nbytes, 0, edl - *nbytes); *nbytes = edl; mifare_cypher_blocks_chained (tag, NULL, NULL, res + offset, *nbytes - offset, MCD_SEND, (AS_NEW == MIFARE_DESFIRE (tag)->authentication_scheme) ? MCO_ENCYPHER : MCO_DECYPHER); break; default: warnx ("Unknown communication settings"); #ifdef WITH_DEBUG abort (); #endif *nbytes = -1; res = NULL; break; } return res; } void * mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int communication_settings) { void *res = data; size_t edl; void *edata = NULL; uint8_t first_cmac_byte; MifareDESFireKey key = MIFARE_DESFIRE (tag)->session_key; if (!key) return data; // Return directly if we just have a status code. if (1 == *nbytes) return res; switch (communication_settings & MDCM_MASK) { case MDCM_PLAIN: if (AS_LEGACY == MIFARE_DESFIRE (tag)->authentication_scheme) break; /* pass through */ case MDCM_MACED: switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: if (communication_settings & MAC_VERIFY) { *nbytes -= key_macing_length (key); edl = enciphered_data_length (tag, *nbytes - 1, communication_settings); edata = malloc (edl); memcpy (edata, data, *nbytes - 1); memset ((uint8_t *)edata + *nbytes - 1, 0, edl - *nbytes + 1); mifare_cypher_blocks_chained (tag, NULL, NULL, edata, edl, MCD_SEND, MCO_ENCYPHER); if (0 != memcmp ((uint8_t *)data + *nbytes - 1, (uint8_t *)edata + edl - 8, 4)) { #ifdef WITH_DEBUG warnx ("MACing not verified"); hexdump ((uint8_t *)data + *nbytes - 1, key_macing_length (key), "Expect ", 0); hexdump ((uint8_t *)edata + edl - 8, key_macing_length (key), "Actual ", 0); #endif MIFARE_DESFIRE (tag)->last_pcd_error = CRYPTO_ERROR; *nbytes = -1; res = NULL; } } break; case AS_NEW: if (!(communication_settings & CMAC_COMMAND)) break; if (communication_settings & CMAC_VERIFY) { if (*nbytes < 9) { // XXX: Can't we avoid abort() -ing? warnx ("No room for CMAC!"); abort (); } first_cmac_byte = ((uint8_t *)data)[*nbytes - 9]; ((uint8_t *)data)[*nbytes - 9] = ((uint8_t *)data)[*nbytes-1]; } int n = (communication_settings & CMAC_VERIFY) ? 8 : 0; cmac (key, MIFARE_DESFIRE (tag)->ivect, ((uint8_t *)data), *nbytes - n, MIFARE_DESFIRE (tag)->cmac); if (communication_settings & CMAC_VERIFY) { ((uint8_t *)data)[*nbytes - 9] = first_cmac_byte; if (0 != memcmp (MIFARE_DESFIRE (tag)->cmac, (uint8_t *)data + *nbytes - 9, 8)) { #ifdef WITH_DEBUG warnx ("CMAC NOT verified :-("); hexdump ((uint8_t *)data + *nbytes - 9, 8, "Expect ", 0); hexdump (MIFARE_DESFIRE (tag)->cmac, 8, "Actual ", 0); #endif MIFARE_DESFIRE (tag)->last_pcd_error = CRYPTO_ERROR; *nbytes = -1; res = NULL; } else { *nbytes -= 8; } } break; } free (edata); break; case MDCM_ENCIPHERED: (*nbytes)--; bool verified = false; int crc_pos; int end_crc_pos; uint8_t x; /* * AS_LEGACY: * ,-----------------+-------------------------------+--------+ * \ BLOCK n-1 | BLOCK n | STATUS | * / PAYLOAD | CRC0 | CRC1 | 0x80? | 0x000000000000 | 0x9100 | * `-----------------+-------------------------------+--------+ * * <------------ DATA ------------> * FRAME = PAYLOAD + CRC(PAYLOAD) + PADDING * * AS_NEW: * ,-------------------------------+-----------------------------------------------+--------+ * \ BLOCK n-1 | BLOCK n | STATUS | * / PAYLOAD | CRC0 | CRC1 | CRC2 | CRC3 | 0x80? | 0x0000000000000000000000000000 | 0x9100 | * `-------------------------------+-----------------------------------------------+--------+ * <----------------------------------- DATA ------------------------------------->| * * <----------------- DATA ----------------> * FRAME = PAYLOAD + CRC(PAYLOAD + STATUS) + PADDING + STATUS * `------------------' */ mifare_cypher_blocks_chained (tag, NULL, NULL, res, *nbytes, MCD_RECEIVE, MCO_DECYPHER); /* * Look for the CRC and ensure it is followed by NULL padding. We * can't start by the end because the CRC is supposed to be 0 when * verified, and accumulating 0's in it should not change it. */ switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: crc_pos = *nbytes - 8 - 1; // The CRC can be over two blocks break; case AS_NEW: /* Move status between payload and CRC */ res = assert_crypto_buffer_size (tag, (*nbytes) + 1); memcpy (res, data, *nbytes); crc_pos = (*nbytes) - 16 - 3; if (crc_pos < 0) { /* Single block */ crc_pos = 0; } memmove ((uint8_t *)res + crc_pos + 1, (uint8_t *)res + crc_pos, *nbytes - crc_pos); ((uint8_t *)res)[crc_pos] = 0x00; crc_pos++; *nbytes += 1; break; } do { uint16_t crc16; uint32_t crc; switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: end_crc_pos = crc_pos + 2; iso14443a_crc (res, end_crc_pos, (uint8_t *)&crc16); crc = crc16; break; case AS_NEW: end_crc_pos = crc_pos + 4; desfire_crc32 (res, end_crc_pos, (uint8_t *)&crc); break; } if (!crc) { verified = true; for (int n = end_crc_pos; n < *nbytes - 1; n++) { uint8_t byte = ((uint8_t *)res)[n]; if (!( (0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)) )) verified = false; } } if (verified) { *nbytes = crc_pos; switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: ((uint8_t *)data)[(*nbytes)++] = 0x00; break; case AS_NEW: /* The status byte was already before the CRC */ break; } } else { switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: break; case AS_NEW: x = ((uint8_t *)res)[crc_pos - 1]; ((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos]; ((uint8_t *)res)[crc_pos] = x; break; } crc_pos++; } } while (!verified && (end_crc_pos < *nbytes)); if (!verified) { #ifdef WITH_DEBUG /* FIXME In some configurations, the file is transmitted PLAIN */ warnx ("CRC not verified in decyphered stream"); #endif MIFARE_DESFIRE (tag)->last_pcd_error = CRYPTO_ERROR; *nbytes = -1; res = NULL; } break; default: warnx ("Unknown communication settings"); #ifdef WITH_DEBUG abort (); #endif *nbytes = -1; res = NULL; break; } return res; } void mifare_cypher_single_block (MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size) { AES_KEY k; uint8_t ovect[MAX_CRYPTO_BLOCK_SIZE]; if (direction == MCD_SEND) { xor (ivect, data, block_size); } else { memcpy (ovect, data, block_size); } uint8_t edata[MAX_CRYPTO_BLOCK_SIZE]; switch (key->type) { case T_DES: switch (operation) { case MCO_ENCYPHER: DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); break; case MCO_DECYPHER: DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); break; } break; case T_3DES: switch (operation) { case MCO_ENCYPHER: DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT); DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); break; case MCO_DECYPHER: DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT); DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); break; } break; case T_3K3DES: switch (operation) { case MCO_ENCYPHER: DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT); DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_ENCRYPT); break; case MCO_DECYPHER: DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_DECRYPT); DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT); DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); break; } break; case T_AES: switch (operation) { case MCO_ENCYPHER: AES_set_encrypt_key (key->data, 8*16, &k); AES_encrypt (data, edata, &k); break; case MCO_DECYPHER: AES_set_decrypt_key (key->data, 8*16, &k); AES_decrypt (data, edata, &k); break; } break; } memcpy (data, edata, block_size); if (direction == MCD_SEND) { memcpy (ivect, data, block_size); } else { xor (ivect, data, block_size); memcpy (ivect, ovect, block_size); } } /* * This function performs all CBC cyphering / deciphering. * * The tag argument may be NULL, in which case both key and ivect shall be set. * When using the tag session_key and ivect for processing data, these * arguments should be set to NULL. * * Because the tag may contain additional data, one may need to call this * function with tag, key and ivect defined. */ void mifare_cypher_blocks_chained (MifareTag tag, MifareDESFireKey key, uint8_t *ivect, uint8_t *data, size_t data_size, MifareCryptoDirection direction, MifareCryptoOperation operation) { size_t block_size; if (tag) { if (!key) key = MIFARE_DESFIRE (tag)->session_key; if (!ivect) ivect = MIFARE_DESFIRE (tag)->ivect; switch (MIFARE_DESFIRE (tag)->authentication_scheme) { case AS_LEGACY: memset (ivect, 0, MAX_CRYPTO_BLOCK_SIZE); break; case AS_NEW: break; } } if (!key || !ivect) abort(); block_size = key_block_size (key); size_t offset = 0; while (offset < data_size) { mifare_cypher_single_block (key, data + offset, ivect, direction, operation, block_size); offset += block_size; } } libfreefare-0.4.0/libfreefare/mifare_desfire_error.c000066400000000000000000000041061225640673100225570ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include "freefare_internal.h" #define EM(e) { e, #e } static struct error_message { uint8_t code; const char *message; } error_messages[] = { EM(OPERATION_OK), EM(NO_CHANGES), EM(OUT_OF_EEPROM_ERROR), EM(ILLEGAL_COMMAND_CODE), EM(INTEGRITY_ERROR), EM(NO_SUCH_KEY), EM(LENGTH_ERROR), EM(PERMISSION_ERROR), EM(PARAMETER_ERROR), EM(APPLICATION_NOT_FOUND), EM(APPL_INTEGRITY_ERROR), EM(AUTHENTICATION_ERROR), EM(ADDITIONAL_FRAME), EM(BOUNDARY_ERROR), EM(PICC_INTEGRITY_ERROR), EM(COMMAND_ABORTED), EM(PICC_DISABLED_ERROR), EM(COUNT_ERROR), EM(DUPLICATE_ERROR), EM(EEPROM_ERROR), EM(FILE_NOT_FOUND), EM(FILE_INTEGRITY_ERROR), EM(CRYPTO_ERROR), { 0, NULL } }; const char * mifare_desfire_error_lookup (uint8_t code) { struct error_message *e = error_messages; while (e->message) { if (e->code == code) return (e->message); e++; } return "Invalid error code"; } uint8_t mifare_desfire_last_pcd_error (MifareTag tag) { if (tag->tag_info->type != DESFIRE) return 0; return MIFARE_DESFIRE (tag)->last_pcd_error; } uint8_t mifare_desfire_last_picc_error (MifareTag tag) { if (tag->tag_info->type != DESFIRE) return 0; return MIFARE_DESFIRE (tag)->last_picc_error; } libfreefare-0.4.0/libfreefare/mifare_desfire_key.3000066400000000000000000000116301225640673100221360ustar00rootroot00000000000000.\" Copyright (C) 2010 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id$ .\" .Dd July 20, 2010 .Dt MIFARE_DESFIRE_KEY 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm mifare_desfire_des_key_new , .Nm mifare_desfire_3des_key_new , .Nm mifare_desfire_3k3des_key_new , .Nm mifare_desfire_aes_key_new , .Nm mifare_desfire_des_key_new_with_version , .Nm mifare_desfire_3des_key_new_with_version , .Nm mifare_desfire_3k3des_key_new_with_version , .Nm mifare_desfire_aes_key_new _with_version, .Nm mifare_desfire_key_get_version , .Nm mifare_desfire_key_set_version , .Nm mifare_desfire_key_free .Nd Mifare DESFire keys Manipulation Functions .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft MifareDESFireKey .Fn mifare_desfire_des_key_new "uint8_t value[8]" .Ft MifareDESFireKey .Fn mifare_desfire_3des_key_new "uint8_t value[16]" .Ft MifareDESFireKey .Fn mifare_desfire_3k3des_key_new "uint8_t value[24]" .Ft MifareDESFireKey .Fn mifare_desfire_aes_key_new "uint8_t value[16]" .Ft MifareDESFireKey .Fn mifare_desfire_des_key_new_with_version "uint8_t value[8]" .Ft MifareDESFireKey .Fn mifare_desfire_3des_key_new_with_version "uint8_t value[16]" .Ft MifareDESFireKey .Fn mifare_desfire_3k3des_key_new_with_version "uint8_t value[24]" .Ft MifareDESFireKey .Fn mifare_desfire_aes_key_new_with_version "uint8_t value[16]" "uint8_t version" .Ft uint8_t .Fn mifare_desfire_key_get_version "MifareDESFireKey key" .Ft void .Fn mifare_desfire_key_set_version "MifareDESFireKey key" "uint8_t version" .Ft void .Fn mifare_desfire_key_free "MifareDESFireKey key" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION The .Fn mifare_desfire_key_* family of functions allows management of Mifare DESFire keys. .Pp The .Fn mifare_desfire_des_key_new , .Fn mifare_desfire_3des_key_new , .Fn mifare_desfire_3k3des_key_new and .Fn mifare_desfire_ades_key_new alocate a new key with the provided data .Va value . The key version is set to .Va 0 . .Pp The .Fn mifare_desfire_des_key_new_with_version , .Fn mifare_desfire_3des_key_new_with_version , .Fn mifare_desfire_3k3des_key_new_with_version and .Fn mifare_desfire_aes_key_new_with_version functions are equivalent to the previous functions except that the key version is read from .Va value (DES, 3DES, 3K3DES) or set to .Va version (AES). .Pp The version of a .Vt MifareDESFireKey can be extracted using .Fn mifare_desfire_key_get_version and changed using .Fn mifare_desfire_key_set_version. .Pp The .Fn mifare_desfire_key_free has to be called for each .Va MifareDESFireKey after usage to reclaim memory. .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES Key allocations functions return the allocaed key of .Va NULL on failure. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr mifare_desfire 3 .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@il4p.org libfreefare-0.4.0/libfreefare/mifare_desfire_key.c000066400000000000000000000115511225640673100222200ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include #include "freefare_internal.h" static inline void update_key_schedules (MifareDESFireKey key); static inline void update_key_schedules (MifareDESFireKey key) { DES_set_key ((DES_cblock *)key->data, &(key->ks1)); DES_set_key ((DES_cblock *)(key->data + 8), &(key->ks2)); if (T_3K3DES == key->type) { DES_set_key ((DES_cblock *)(key->data + 16), &(key->ks3)); } } MifareDESFireKey mifare_desfire_des_key_new (uint8_t value[8]) { uint8_t data[8]; memcpy (data, value, 8); for (int n=0; n < 8; n++) data[n] &= 0xfe; return mifare_desfire_des_key_new_with_version (data); } MifareDESFireKey mifare_desfire_des_key_new_with_version (uint8_t value[8]) { MifareDESFireKey key; if ((key = malloc (sizeof (struct mifare_desfire_key)))) { key->type = T_DES; memcpy (key->data, value, 8); memcpy (key->data+8, value, 8); update_key_schedules (key); } return key; } MifareDESFireKey mifare_desfire_3des_key_new (uint8_t value[16]) { uint8_t data[16]; memcpy (data, value, 16); for (int n=0; n < 8; n++) data[n] &= 0xfe; for (int n=8; n < 16; n++) data[n] |= 0x01; return mifare_desfire_3des_key_new_with_version (data); } MifareDESFireKey mifare_desfire_3des_key_new_with_version (uint8_t value[16]) { MifareDESFireKey key; if ((key = malloc (sizeof (struct mifare_desfire_key)))) { key->type = T_3DES; memcpy (key->data, value, 16); update_key_schedules (key); } return key; } MifareDESFireKey mifare_desfire_3k3des_key_new (uint8_t value[24]) { uint8_t data[24]; memcpy (data, value, 24); for (int n=0; n < 8; n++) data[n] &= 0xfe; return mifare_desfire_3k3des_key_new_with_version (data); } MifareDESFireKey mifare_desfire_3k3des_key_new_with_version (uint8_t value[24]) { MifareDESFireKey key; if ((key = malloc (sizeof (struct mifare_desfire_key)))) { key->type = T_3K3DES; memcpy (key->data, value, 24); update_key_schedules (key); } return key; } MifareDESFireKey mifare_desfire_aes_key_new (uint8_t value[16]) { return mifare_desfire_aes_key_new_with_version (value, 0); } MifareDESFireKey mifare_desfire_aes_key_new_with_version (uint8_t value[16], uint8_t version) { MifareDESFireKey key; if ((key = malloc (sizeof (struct mifare_desfire_key)))) { memcpy (key->data, value, 16); key->type = T_AES; key->aes_version = version; } return key; } uint8_t mifare_desfire_key_get_version (MifareDESFireKey key) { uint8_t version = 0; for (int n = 0; n < 8; n++) { version |= ((key->data[n] & 1) << (7 - n)); } return version; } void mifare_desfire_key_set_version (MifareDESFireKey key, uint8_t version) { for (int n = 0; n < 8; n++) { uint8_t version_bit = ((version & (1 << (7-n))) >> (7-n)); key->data[n] &= 0xfe; key->data[n] |= version_bit; if (key->type == T_DES) { key->data[n+8] = key->data[n]; } else { // Write ~version to avoid turning a 3DES key into a DES key key->data[n+8] &= 0xfe; key->data[n+8] |= ~version_bit; } } } MifareDESFireKey mifare_desfire_session_key_new (uint8_t rnda[8], uint8_t rndb[8], MifareDESFireKey authentication_key) { MifareDESFireKey key = NULL; uint8_t buffer[24]; switch (authentication_key->type) { case T_DES: memcpy (buffer, rnda, 4); memcpy (buffer+4, rndb, 4); key = mifare_desfire_des_key_new_with_version (buffer); break; case T_3DES: memcpy (buffer, rnda, 4); memcpy (buffer+4, rndb, 4); memcpy (buffer+8, rnda+4, 4); memcpy (buffer+12, rndb+4, 4); key = mifare_desfire_3des_key_new_with_version (buffer); break; case T_3K3DES: memcpy (buffer, rnda, 4); memcpy (buffer+4, rndb, 4); memcpy (buffer+8, rnda+6, 4); memcpy (buffer+12, rndb+6, 4); memcpy (buffer+16, rnda+12, 4); memcpy (buffer+20, rndb+12, 4); key = mifare_desfire_3k3des_key_new (buffer); break; case T_AES: memcpy (buffer, rnda, 4); memcpy (buffer+4, rndb, 4); memcpy (buffer+8, rnda+12, 4); memcpy (buffer+12, rndb+12, 4); key = mifare_desfire_aes_key_new (buffer); break; } return key; } void mifare_desfire_key_free (MifareDESFireKey key) { free (key); } libfreefare-0.4.0/libfreefare/mifare_ultralight.3000066400000000000000000000072241225640673100220300ustar00rootroot00000000000000.\" Copyright (C) 2010 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id$ .\" .Dd March 30, 2010 .Dt MIFARE_ULTRALIGHT 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm mifare_ultralight_connect , .Nm mifare_ultralight_disconnect , .Nm mifare_ultralight_read , .Nm mifare_ultralight_write , .Nd Mifare UltraLight Manipulation Functions .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft int .Fn mifare_ultralight_connect "MifareTag tag" .Ft int .Fn mifare_ultralight_disconnect "MifareTag tag" .Ft int .Fn mifare_ultralight_read "MifareTag tag" "const MifareUltralightPageNumber page" "MifareUltralightPage *data" .Ft int .Fn mifare_ultralight_write "MifareTag tag" "const MifareUltralightPageNumber page" "const MifareUltralightPage data" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION The .Fn mifare_ultralight_* functions allows management of Mifare UltraLight tags. .Pp The .Fn mifare_ultralight_connect function activates the specified .Vt tag . .Pp A .Vt page of .Vt data can be read from a .Vt tag using .Fn mifare_ultralight_read , and written using .Fn mifare_ultralight_write . .Pp After usage, a .Vt tag is deactivated using .Fn mifare_ultralight_disconnect . .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES Unless stated otherwise, all functions return a value greater than or equal to .Va 0 on success or .Va -1 on failure. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr freefare 3 .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@blogreen.org .An Romuald Conty Aq romuald@libnfc.org libfreefare-0.4.0/libfreefare/mifare_ultralight.c000066400000000000000000000204021225640673100221010ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ /* * This implementation was written based on information provided by the * following documents: * * Contactless Single-trip Ticket IC * MF0 IC U1 * Functional Specification * Revision 3.0 * March 2003 */ #include "config.h" #if defined(HAVE_SYS_TYPES_H) # include #endif #include #include #include #ifdef WITH_DEBUG # include #endif #include #include "freefare_internal.h" #define ASSERT_VALID_PAGE(tag, page, mode_write) \ do { \ if (IS_MIFARE_ULTRALIGHT_C(tag)) { \ if (mode_write) { \ if (page >= MIFARE_ULTRALIGHT_C_PAGE_COUNT) return errno = EINVAL, -1; \ } else { \ if (page >= MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ) return errno = EINVAL, -1; \ } \ } else { \ if (page >= MIFARE_ULTRALIGHT_PAGE_COUNT) return errno = EINVAL, -1; \ } \ } while (0) #define ULTRALIGHT_TRANSCEIVE(tag, msg, res) \ do { \ errno = 0; \ DEBUG_XFER (msg, __##msg##_n, "===> "); \ int _res; \ if ((_res = nfc_initiator_transceive_bytes (tag->device, msg, __##msg##_n, res, __##res##_size, 0)) < 0) { \ return errno = EIO, -1; \ } \ __##res##_n = _res; \ DEBUG_XFER (res, __##res##_n, "<=== "); \ } while (0) #define ULTRALIGHT_TRANSCEIVE_RAW(tag, msg, res) \ do { \ errno = 0; \ if (nfc_device_set_property_bool (tag->device, NP_EASY_FRAMING, false) < 0) { \ errno = EIO; \ return -1; \ } \ DEBUG_XFER (msg, __##msg##_n, "===> "); \ int _res; \ if ((_res = nfc_initiator_transceive_bytes (tag->device, msg, __##msg##_n, res, __##res##_size, 0)) < 0) { \ nfc_device_set_property_bool (tag->device, NP_EASY_FRAMING, true); \ return errno = EIO, -1; \ } \ __##res##_n = _res; \ DEBUG_XFER (res, __##res##_n, "<=== "); \ if (nfc_device_set_property_bool (tag->device, NP_EASY_FRAMING, true) < 0) { \ errno = EIO; \ return -1; \ } \ } while (0) /* * Memory management functions. */ /* * Allocates and initialize a MIFARE UltraLight tag. */ MifareTag mifare_ultralight_tag_new (void) { return malloc (sizeof (struct mifare_ultralight_tag)); } /* * Free the provided tag. */ void mifare_ultralight_tag_free (MifareTag tag) { free (tag); } /* * MIFARE card communication preparation functions * * The following functions send NFC commands to the initiator to prepare * communication with a MIFARE card, and perform required cleanups after using * the target. */ /* * Establish connection to the provided tag. */ int mifare_ultralight_connect (MifareTag tag) { ASSERT_INACTIVE (tag); ASSERT_MIFARE_ULTRALIGHT (tag); nfc_target pnti; nfc_modulation modulation = { .nmt = NMT_ISO14443A, .nbr = NBR_106 }; if (nfc_initiator_select_passive_target (tag->device, modulation, tag->info.abtUid, tag->info.szUidLen, &pnti) >= 0) { tag->active = 1; for (int i = 0; i < MIFARE_ULTRALIGHT_MAX_PAGE_COUNT; i++) MIFARE_ULTRALIGHT(tag)->cached_pages[i] = 0; } else { errno = EIO; return -1; } return 0; } /* * Terminate connection with the provided tag. */ int mifare_ultralight_disconnect (MifareTag tag) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_ULTRALIGHT (tag); if (nfc_initiator_deselect_target (tag->device) >= 0) { tag->active = 0; } else { errno = EIO; return -1; } return 0; } /* * Card manipulation functions * * The following functions perform direct communication with the connected * MIFARE UltraLight tag. */ /* * Read data from the provided MIFARE tag. */ int mifare_ultralight_read (MifareTag tag, MifareUltralightPageNumber page, MifareUltralightPage *data) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_ULTRALIGHT (tag); ASSERT_VALID_PAGE (tag, page, false); if (!MIFARE_ULTRALIGHT(tag)->cached_pages[page]) { BUFFER_INIT (cmd, 2); BUFFER_ALIAS (res, MIFARE_ULTRALIGHT(tag)->cache[page], sizeof(MifareUltralightPage) * 4); BUFFER_APPEND (cmd, 0x30); BUFFER_APPEND (cmd, page); ULTRALIGHT_TRANSCEIVE (tag, cmd, res); /* Handle wrapped pages */ int iPageCount; if (IS_MIFARE_ULTRALIGHT_C(tag)) { iPageCount = MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ; } else { iPageCount = MIFARE_ULTRALIGHT_PAGE_COUNT; } for (int i = iPageCount; i <= page + 3; i++) { memcpy (MIFARE_ULTRALIGHT(tag)->cache[i % iPageCount], MIFARE_ULTRALIGHT(tag)->cache[i], sizeof (MifareUltralightPage)); } /* Mark pages as cached */ for (int i = page; i <= page + 3; i++) { MIFARE_ULTRALIGHT(tag)->cached_pages[i % iPageCount] = 1; } } memcpy (data, MIFARE_ULTRALIGHT(tag)->cache[page], sizeof (*data)); return 0; } /* * Read data to the provided MIFARE tag. */ int mifare_ultralight_write (MifareTag tag, const MifareUltralightPageNumber page, const MifareUltralightPage data) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_ULTRALIGHT (tag); ASSERT_VALID_PAGE (tag, page, true); BUFFER_INIT (cmd, 6); BUFFER_INIT (res, 1); BUFFER_APPEND (cmd, 0xA2); BUFFER_APPEND (cmd, page); BUFFER_APPEND_BYTES (cmd, data, sizeof (MifareUltralightPage)); ULTRALIGHT_TRANSCEIVE (tag, cmd, res); /* Invalidate page in cache */ MIFARE_ULTRALIGHT(tag)->cached_pages[page] = 0; return 0; } /* * Authenticate to the provided MIFARE tag. */ int mifare_ultralightc_authenticate (MifareTag tag, const MifareDESFireKey key) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_ULTRALIGHT_C (tag); BUFFER_INIT (cmd1, 2); BUFFER_INIT (res, 9); BUFFER_APPEND (cmd1, 0x1A); BUFFER_APPEND (cmd1, 0x00); ULTRALIGHT_TRANSCEIVE_RAW(tag, cmd1, res); uint8_t PICC_E_RndB[8]; memcpy (PICC_E_RndB, res+1, 8); uint8_t PICC_RndB[8]; memcpy (PICC_RndB, PICC_E_RndB, 8); uint8_t ivect[8]; memset (ivect, '\0', sizeof (ivect)); mifare_cypher_single_block (key, PICC_RndB, ivect, MCD_RECEIVE, MCO_DECYPHER, 8); uint8_t PCD_RndA[8]; DES_random_key ((DES_cblock*)&PCD_RndA); uint8_t PCD_r_RndB[8]; memcpy (PCD_r_RndB, PICC_RndB, 8); rol (PCD_r_RndB, 8); uint8_t token[16]; memcpy (token, PCD_RndA, 8); memcpy (token+8, PCD_r_RndB, 8); size_t offset = 0; while (offset < 16) { mifare_cypher_single_block (key, token + offset, ivect, MCD_SEND, MCO_ENCYPHER, 8); offset += 8; } BUFFER_INIT (cmd2, 17); BUFFER_APPEND (cmd2, 0xAF); BUFFER_APPEND_BYTES (cmd2, token, 16); ULTRALIGHT_TRANSCEIVE_RAW(tag, cmd2, res); uint8_t PICC_E_RndA_s[8]; memcpy (PICC_E_RndA_s, res+1, 8); uint8_t PICC_RndA_s[8]; memcpy (PICC_RndA_s, PICC_E_RndA_s, 8); mifare_cypher_single_block (key, PICC_RndA_s, ivect, MCD_RECEIVE, MCO_DECYPHER, 8); uint8_t PCD_RndA_s[8]; memcpy (PCD_RndA_s, PCD_RndA, 8); rol (PCD_RndA_s, 8); if (0 != memcmp (PCD_RndA_s, PICC_RndA_s, 8)) { return -1; } // XXX Should we store the state "authenticated" in the tag struct?? return 0; } /* * Callback for freefare_tag_new to test presence of a MIFARE UltralightC on the reader. */ bool is_mifare_ultralightc_on_reader (nfc_device *device, nfc_iso14443a_info nai) { int ret; uint8_t cmd_step1[2]; uint8_t res_step1[9]; cmd_step1[0] = 0x1A; cmd_step1[1] = 0x00; nfc_target pnti; nfc_modulation modulation = { .nmt = NMT_ISO14443A, .nbr = NBR_106 }; nfc_initiator_select_passive_target (device, modulation, nai.abtUid, nai.szUidLen, &pnti); nfc_device_set_property_bool (device, NP_EASY_FRAMING, false); ret = nfc_initiator_transceive_bytes (device, cmd_step1, sizeof (cmd_step1), res_step1, sizeof(res_step1), 0); nfc_device_set_property_bool (device, NP_EASY_FRAMING, true); nfc_initiator_deselect_target (device); return ret >= 0; } libfreefare-0.4.0/libfreefare/tlv.3000066400000000000000000000070051225640673100171300ustar00rootroot00000000000000.\" Copyright (C) 2010 Romain Tartiere .\" .\" This program is free software: you can redistribute it and/or modify it .\" under the terms of the GNU Lesser General Public License as published by the .\" Free Software Foundation, either version 3 of the License, or (at your .\" option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" along with this program. If not, see .\" .\" $Id$ .\" .Dd March 30, 2010 .Dt TLV 3 .Os .\" _ _ .\" | \ | | __ _ _ __ ___ ___ .\" | \| |/ _` | '_ ` _ \ / _ \ .\" | |\ | (_| | | | | | | __/ .\" |_| \_|\__,_|_| |_| |_|\___| .\" .Sh NAME .Nm tlv_encode , .Nm tlv_decode .Nd TLV Manipulation Functions .\" _ _ _ .\" | | (_) |__ _ __ __ _ _ __ _ _ .\" | | | | '_ \| '__/ _` | '__| | | | .\" | |___| | |_) | | | (_| | | | |_| | .\" |_____|_|_.__/|_| \__,_|_| \__, | .\" |___/ .Sh LIBRARY Mifare card manipulation library (libfreefare, \-lfreefare) .\" ____ _ .\" / ___| _ _ _ __ ___ _ __ ___(_)___ .\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| .\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS .In freefare.h .Ft "uint8_t *" .Fn tlv_encode "const uint8_t type" "const uint8_t *istream" "uint16_t isize" "size_t *osize" .Ft "uint8_t *" .Fn tlv_decode "const uint8_t *istream" "uint8_t *type" "uint16_t *size" .\" ____ _ _ _ .\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ .\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ .\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | .\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| .\" |_| .Sh DESCRIPTION The .Fn tlv_encode and .Fn tlv_decode functions are helpers to manipulate TLV (Text-Length-Value) data. .Pp The .Fn tlv_encode function converts the .Ar isize bytes long .Ar istream message into a TLV stream of type .Ar type and set the value of .Ar osize to the length of the returned stream. .Pp The .Fn tlv_decode function converts the .Ar istream TLV stream and set the .Ar type argument according to the type of the stream, and set the .Ar size argument to the length of the returned stream. .\" ____ _ _ .\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ .\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| .\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ .\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ .\" .Sh RETURN VALUES Both functions return memory allocated using .Xr malloc 3 which should be reclaimed using .Xr free 3 after usage. .\" ____ _ .\" / ___| ___ ___ __ _| |___ ___ .\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ .\" ___) | __/ __/ | (_| | \__ \ (_) | .\" |____/ \___|\___| \__,_|_|___/\___/ .\" .Sh SEE ALSO .Xr freefare 3 , .Xr malloc 3 , .Xr free 3 .\" _ _ _ .\" / \ _ _| |_| |__ ___ _ __ ___ .\" / _ \| | | | __| '_ \ / _ \| '__/ __| .\" / ___ \ |_| | |_| | | | (_) | | \__ \ .\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ .\" .Sh AUTHORS .An Romain Tartiere Aq romain@blogreen.org .An Romuald Conty Aq romuald@libnfc.org libfreefare-0.4.0/libfreefare/tlv.c000066400000000000000000000107051225640673100172110ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ /* * This implementation was written based on information provided by the * following documents: * * NFC Forum - Type 1 Tag Operation Specification * NFCForum-TS-Type-1-Tag_1.0 * 2007-07-09 */ #include "config.h" #if defined(HAVE_SYS_TYPES_H) # include #endif #if defined(HAVE_SYS_ENDIAN_H) # include #endif #if defined(HAVE_ENDIAN_H) # include #endif #if defined(HAVE_COREFOUNDATION_COREFOUNDATION_H) # include #endif #if defined(HAVE_BYTESWAP_H) # include #endif #include #include #include #include #include "freefare_internal.h" #define TLV_TERMINATOR 0xFE size_t tlv_record_length (const uint8_t *stream, size_t *field_length_size, size_t *field_value_size); uint8_t *tlv_next (uint8_t *stream); size_t tlv_sequence_length (uint8_t *stream); /* * TLV (Type Length Value) Manipulation Functions. */ /* * Encode data stream into TLV. */ uint8_t * tlv_encode (const uint8_t type, const uint8_t *istream, uint16_t isize, size_t *osize) { uint8_t *res; off_t n = 0; if (osize) *osize = 0; if (isize == 0xffff) /* RFU */ return NULL; if ((res = malloc (1 + ((isize > 254) ? 3 : 1) + isize + 1))) { /* type + size + payload + terminator */ res[n++] = type; if (isize > 254) { res[n++] = 0xff; uint16_t size_be = htobe16 (isize); memcpy (res + n, &size_be, sizeof (uint16_t)); n += 2; } else { res[n++] = (uint8_t)isize; } memcpy (res + n, istream, isize); n += isize; res[n++] = TLV_TERMINATOR; if (osize) *osize = n; } return res; } /* * Decode TLV from data stream. */ uint8_t * tlv_decode (const uint8_t *istream, uint8_t *type, uint16_t *size) { size_t fls = 0; size_t fvs = 0; uint8_t *res = NULL; if (type) *type = istream[0]; tlv_record_length (istream, &fls, &fvs); if (size) { *size = fvs; } if ((res = malloc (fvs))) { memcpy (res, istream + 1 + fls, fvs); } return res; } /* * Length of a TLV field */ size_t tlv_record_length (const uint8_t *stream, size_t *field_length_size, size_t *field_value_size) { size_t fls = 0; size_t fvs = 0; switch (stream[0]) { case 0x00: case 0xfe: break; case 0x01: case 0x02: case 0x03: default: // FIXME Not supported. if (stream[1] == 0xff) { uint16_t be_size; memcpy (&be_size, stream + 2, sizeof (uint16_t)); fls = 3; fvs = be16toh(be_size); } else { fls = 1; fvs = stream[1]; } break; } if (field_length_size) *field_length_size = fls; if (field_value_size) *field_value_size = fvs; return 1 + fls + fvs; } /* * Get a pointer to the next record in the provided TLV sequence. * | 0x03 | 0x02 | 0xbe | 0xef | 0x00 | 0x00 | 0xfe | * First call ---' | | * Second call -------------------------------' | * Third call --------------------------------------' * Fourth call NULL */ uint8_t * tlv_next (uint8_t *stream) { uint8_t *res = NULL; if (stream[0] != TLV_TERMINATOR) res = stream + tlv_record_length (stream, NULL, NULL); return res; } /* * Full-length of all TLV fields. */ size_t tlv_sequence_length (uint8_t *stream) { size_t res = 0; do { res += tlv_record_length (stream, NULL, NULL); } while ((stream = tlv_next (stream))); return res; } /* * Append two TLV. Acts like realloc(3). */ uint8_t * tlv_append (uint8_t *a, uint8_t *b) { size_t a_size = tlv_sequence_length (a); size_t b_size = tlv_sequence_length (b); size_t new_size = a_size + b_size - 1; if ((a = realloc (a, new_size))) { memcpy (a + a_size - 1, b, b_size); } return a; } libfreefare-0.4.0/m4/000077500000000000000000000000001225640673100143075ustar00rootroot00000000000000libfreefare-0.4.0/m4/.gitignore000066400000000000000000000001071225640673100162750ustar00rootroot00000000000000# Ignore everything in this directory * # Except this file !.gitignore libfreefare-0.4.0/test/000077500000000000000000000000001225640673100147465ustar00rootroot00000000000000libfreefare-0.4.0/test/Makefile.am000066400000000000000000000064321225640673100170070ustar00rootroot00000000000000# $Id$ AM_CPPFLAGS = $(CUTTER_CFLAGS) -I$(top_srcdir)/libfreefare LIBS = $(CUTTER_LIBS) SUBDIRS = common . AM_CFLAGS = @LIBNFC_CFLAGS@ if WITH_CUTTER TESTS = run-test.sh TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER="$(CUTTER)" cutter_unit_test_libs = \ test_mad.la \ test_mifare_application.la \ test_mifare_classic.la \ test_mifare_classic_create_trailer_block.la \ test_mifare_classic_sector_boundaries.la \ test_mifare_desfire.la \ test_mifare_desfire_aes.la \ test_mifare_desfire_aid.la \ test_mifare_desfire_des.la \ test_mifare_desfire_ev1.la \ test_mifare_desfire_key.la \ test_mifare_ultralight.la \ test_tlv.la if WITH_DEBUG noinst_LTLIBRARIES = $(cutter_unit_test_libs) else check_LTLIBRARIES = $(cutter_unit_test_libs) endif AM_LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined test_mad_la_SOURCES = test_mad.c test_mad_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_mifare_application_la_SOURCES = test_mifare_application.c test_mifare_application_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_mifare_classic_la_SOURCES = test_mifare_classic.c \ test_mifare_classic_mad.c \ mifare_classic_fixture.c \ mifare_classic_fixture.h test_mifare_classic_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_mifare_classic_create_trailer_block_la_SOURCES = test_mifare_classic_create_trailer_block.c test_mifare_classic_create_trailer_block_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_mifare_classic_sector_boundaries_la_SOURCES = test_mifare_classic_sector_boundaries.c test_mifare_classic_sector_boundaries_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_mifare_desfire_la_SOURCES = test_mifare_desfire.c \ mifare_desfire_fixture.c \ mifare_desfire_fixture.h test_mifare_desfire_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la \ $(top_builddir)/test/common/libtestcommon.la test_mifare_desfire_aes_la_SOURCES = test_mifare_desfire_aes.c test_mifare_desfire_aes_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_mifare_desfire_aid_la_SOURCES = test_mifare_desfire_aid.c test_mifare_desfire_aid_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_mifare_desfire_des_la_SOURCES = test_mifare_desfire_des.c test_mifare_desfire_des_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_mifare_desfire_ev1_la_SOURCES = test_mifare_desfire_ev1.c \ test_mifare_desfire_ev1_3des.c \ test_mifare_desfire_ev1_3k3des.c \ test_mifare_desfire_ev1_aes.c \ test_mifare_desfire_ev1_iso.c \ mifare_desfire_ev1_fixture.c \ mifare_desfire_ev1_fixture.h test_mifare_desfire_ev1_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la \ $(top_builddir)/test/common/libtestcommon.la test_mifare_desfire_key_la_SOURCES = test_mifare_desfire_key.c test_mifare_desfire_key_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_mifare_ultralight_la_SOURCES = test_mifare_ultralight.c \ mifare_ultralight_fixture.c \ mifare_ultralight_fixture.h test_mifare_ultralight_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la test_tlv_la_SOURCES = test_tlv.c test_tlv_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la echo-cutter: @echo $(CUTTER) EXTRA_DIST = run-test.sh CLEANFILES = *.gcno endif libfreefare-0.4.0/test/common/000077500000000000000000000000001225640673100162365ustar00rootroot00000000000000libfreefare-0.4.0/test/common/Makefile.am000066400000000000000000000006041225640673100202720ustar00rootroot00000000000000# $Id$ AM_CPPFLAGS = $(CUTTER_CFLAGS) -I$(top_srcdir)/libfreefare AM_CFLAGS = @LIBNFC_CFLAGS@ if WITH_CUTTER cutter_unit_test_libs = \ libtestcommon.la if WITH_DEBUG noinst_LTLIBRARIES = $(cutter_unit_test_libs) else check_LTLIBRARIES = $(cutter_unit_test_libs) endif libtestcommon_la_SOURCES = mifare_desfire_auto_authenticate.c \ mifare_desfire_auto_authenticate.h endif libfreefare-0.4.0/test/common/mifare_desfire_auto_authenticate.c000066400000000000000000000055001225640673100251340ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "mifare_desfire_auto_authenticate.h" uint8_t key_data_null[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t key_data_des[8] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; uint8_t key_data_3des[16] = { 'C', 'a', 'r', 'd', ' ', 'M', 'a', 's', 't', 'e', 'r', ' ', 'K', 'e', 'y', '!' }; uint8_t key_data_aes[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t key_data_3k3des[24] = { 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const uint8_t key_data_aes_version = 0x42; void mifare_desfire_auto_authenticate (MifareTag tag, uint8_t key_no) { /* Determine which key is currently the master one */ uint8_t key_version; int res = mifare_desfire_get_key_version (tag, key_no, &key_version); cut_assert_equal_int (0, res, cut_message ("mifare_desfire_get_key_version()")); MifareDESFireKey key; switch (key_version) { case 0x00: key = mifare_desfire_des_key_new_with_version (key_data_null); break; case 0x42: key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version); break; case 0xAA: key = mifare_desfire_des_key_new_with_version (key_data_des); break; case 0xC7: key = mifare_desfire_3des_key_new_with_version (key_data_3des); break; case 0x55: key = mifare_desfire_3k3des_key_new_with_version (key_data_3k3des); break; default: cut_fail ("Unknown master key."); } cut_assert_not_null (key, cut_message ("Cannot allocate key")); /* Authenticate with this key */ switch (key_version) { case 0x00: case 0xAA: case 0xC7: res = mifare_desfire_authenticate (tag, key_no, key); break; case 0x55: res = mifare_desfire_authenticate_iso (tag, key_no, key); break; case 0x42: res = mifare_desfire_authenticate_aes (tag, key_no, key); break; } cut_assert_equal_int (0, res, cut_message ("mifare_desfire_authenticate()")); mifare_desfire_key_free (key); } libfreefare-0.4.0/test/common/mifare_desfire_auto_authenticate.h000066400000000000000000000021231225640673100251370ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #ifndef _MIFARE_DESFIRE_AUTO_AUTHENTICATE_H #define _MIFARE_DESFIRE_AUTO_AUTHENTICATE_H extern uint8_t key_data_null[8]; extern uint8_t key_data_des[8]; extern uint8_t key_data_3des[16]; extern uint8_t key_data_aes[16]; extern uint8_t key_data_3k3des[24]; extern const uint8_t key_data_aes_version; void mifare_desfire_auto_authenticate (MifareTag tag, uint8_t key_no); #endif libfreefare-0.4.0/test/mifare_classic_fixture.c000066400000000000000000000045411225640673100216300ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere * Copyright (C) 2013, Romuald Conty * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "mifare_classic_fixture.h" static nfc_context *context; static nfc_device *device = NULL; static MifareTag *tags = NULL; MifareTag tag = NULL; void cut_setup (void) { int res; nfc_connstring devices[8]; size_t device_count; nfc_init (&context); cut_assert_not_null (context, cut_message ("Unable to init libnfc (malloc)")); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) cut_omit ("No device found"); for (size_t i = 0; i < device_count; i++) { device = nfc_open (context, devices[i]); if (!device) cut_omit ("nfc_open() failed."); tags = freefare_get_tags (device); cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); tag = NULL; for (int i=0; tags[i]; i++) { if ((freefare_get_tag_type(tags[i]) == CLASSIC_1K) || (freefare_get_tag_type(tags[i]) == CLASSIC_4K)) { tag = tags[i]; res = mifare_classic_connect (tag); cut_assert_equal_int (0, res, cut_message ("mifare_classic_connect() failed")); return; } } nfc_close (device); device = NULL; freefare_free_tags (tags); tags = NULL; } cut_omit ("No MIFARE Classic tag on NFC device"); } void cut_teardown (void) { if (tag) mifare_classic_disconnect (tag); if (tags) { freefare_free_tags (tags); tags = NULL; } if (device) nfc_close (device); nfc_exit (context); } libfreefare-0.4.0/test/mifare_classic_fixture.h000066400000000000000000000013721225640673100216340ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ extern MifareTag tag; libfreefare-0.4.0/test/mifare_desfire_ev1_fixture.c000066400000000000000000000055701225640673100224060ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere * Copyright (C) 2013, Romuald Conty * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "mifare_desfire_ev1_fixture.h" static nfc_context *context; static nfc_device *device = NULL; static MifareTag *tags = NULL; MifareTag tag = NULL; void cut_setup (void) { int res; nfc_connstring devices[8]; size_t device_count; nfc_init (&context); cut_assert_not_null (context, cut_message ("Unable to init libnfc (malloc)")); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) cut_omit ("No device found"); for (size_t i = 0; i < device_count; i++) { device = nfc_open (context, devices[i]); if (!device) cut_omit ("nfc_open() failed."); tags = freefare_get_tags (device); cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); tag = NULL; for (int i=0; tags[i]; i++) { if (freefare_get_tag_type(tags[i]) == DESFIRE) { tag = tags[i]; res = mifare_desfire_connect (tag); cut_assert_equal_int (0, res, cut_message ("mifare_desfire_connect() failed")); struct mifare_desfire_version_info version_info; res = mifare_desfire_get_version (tag, &version_info); cut_assert_equal_int (0, res, cut_message ("mifare_desfire_get_version")); if (version_info.hardware.storage_size < 0x18) { cut_omit ("DESFire EV1 tests require at least a 4K card"); } if ((version_info.hardware.version_major >= 1) && (version_info.software.version_major >= 1)) { return; } mifare_desfire_disconnect (tag); } } nfc_close (device); device = NULL; freefare_free_tags (tags); tags = NULL; } cut_omit ("No MIFARE DESFire EV1 tag on NFC device"); } void cut_teardown (void) { if (tag) mifare_desfire_disconnect (tag); if (tags) { freefare_free_tags (tags); tags = NULL; } if (device) nfc_close (device); nfc_exit (context); } libfreefare-0.4.0/test/mifare_desfire_ev1_fixture.h000066400000000000000000000013531225640673100224060ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ extern MifareTag tag; libfreefare-0.4.0/test/mifare_desfire_fixture.c000066400000000000000000000047431225640673100216340ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere * Copyright (C) 2013, Romuald Conty * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "mifare_desfire_fixture.h" static nfc_context *context; static nfc_device *device = NULL; static MifareTag *tags = NULL; MifareTag tag = NULL; void cut_setup (void) { int res; nfc_connstring devices[8]; size_t device_count; nfc_init (&context); cut_assert_not_null (context, cut_message ("Unable to init libnfc (malloc)")); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) cut_omit ("No device found"); for (size_t i = 0; i < device_count; i++) { device = nfc_open (context, devices[i]); if (!device) cut_omit ("nfc_open() failed."); tags = freefare_get_tags (device); cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); tag = NULL; for (int i=0; tags[i]; i++) { if (freefare_get_tag_type(tags[i]) == DESFIRE) { tag = tags[i]; res = mifare_desfire_connect (tag); cut_assert_equal_int (0, res, cut_message ("mifare_desfire_connect() failed")); struct mifare_desfire_version_info version_info; res = mifare_desfire_get_version (tag, &version_info); cut_assert_equal_int (0, res, cut_message ("mifare_desfire_get_version")); if (version_info.hardware.storage_size < 0x18) { cut_omit ("DESFire tests require at least a 4K card"); } return; } } nfc_close (device); device = NULL; freefare_free_tags (tags); tags = NULL; } cut_omit ("No MIFARE DESFire tag on NFC device"); } void cut_teardown (void) { if (tag) mifare_desfire_disconnect (tag); if (tags) { freefare_free_tags (tags); tags = NULL; } if (device) nfc_close (device); nfc_exit (context); } libfreefare-0.4.0/test/mifare_desfire_fixture.h000066400000000000000000000013531225640673100216330ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ extern MifareTag tag; libfreefare-0.4.0/test/mifare_ultralight_fixture.c000066400000000000000000000045641225640673100223730ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere * Copyright (C) 2013, Romuald Conty * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "mifare_ultralight_fixture.h" static nfc_context *context; static nfc_device *device = NULL; static MifareTag *tags = NULL; MifareTag tag = NULL; void cut_setup (void) { int res; nfc_connstring devices[8]; size_t device_count; nfc_init (&context); cut_assert_not_null (context, cut_message ("Unable to init libnfc (malloc)")); device_count = nfc_list_devices (context, devices, 8); if (device_count <= 0) cut_omit ("No device found"); for (size_t i = 0; i < device_count; i++) { device = nfc_open (context, devices[i]); if (!device) cut_omit ("nfc_open() failed."); tags = freefare_get_tags (device); cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); tag = NULL; for (int i=0; tags[i]; i++) { if ((freefare_get_tag_type(tags[i]) == ULTRALIGHT) || (freefare_get_tag_type(tags[i]) == ULTRALIGHT_C)) { tag = tags[i]; res = mifare_ultralight_connect (tag); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_connect() failed")); return; } } nfc_close (device); device = NULL; freefare_free_tags (tags); tags = NULL; } cut_omit ("No MIFARE UltraLight tag on NFC device"); } void cut_teardown (void) { if (tag) mifare_ultralight_disconnect (tag); if (tags) { freefare_free_tags (tags); tags = NULL; } if (device) nfc_close (device); nfc_exit (context); } libfreefare-0.4.0/test/mifare_ultralight_fixture.h000066400000000000000000000013531225640673100223710ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ extern MifareTag tag; libfreefare-0.4.0/test/run-test.sh000077500000000000000000000007751225640673100170770ustar00rootroot00000000000000#!/bin/sh export BASE_DIR="`dirname $0`" if test -z "$NO_MAKE"; then make -C "$BASE_DIR/../" > /dev/null || exit 1 fi if test -z "$CUTTER"; then CUTTER="`make -s -C "$BASE_DIR" echo-cutter`" fi "$CUTTER" --keep-opening-modules -s "$BASE_DIR" "$@" "$BASE_DIR" # ^^^^^^^^^^^^^^^^^^^^^^ # FIXME: Remove this workaround once cutter has been fixed upstream. # Bug report: # http://sourceforge.net/mailarchive/forum.php?thread_name=20100626123941.GA258%40blogreen.org&forum_name=cutter-users-en libfreefare-0.4.0/test/test_mad.c000066400000000000000000000171641225640673100167230ustar00rootroot00000000000000/*- * Copyright (C) 2009, 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "freefare_internal.h" void test_mad (void) { int res; Mad mad = mad_new (1); cut_assert_not_null (mad, cut_message ("Can create a new MAD")); cut_assert_equal_int (1, mad_get_version (mad), cut_message ("Wrong default MAD version")); mad_set_version (mad, 2); cut_assert_equal_int (2, mad_get_version (mad), cut_message ("Can't change MAD version")); cut_assert_equal_int (0, mad_get_card_publisher_sector (mad), cut_message ("Wrong default MAD publisher")); res = mad_set_card_publisher_sector (mad, 13); cut_assert_equal_int (0, res, cut_message ("mad_set_card_publisher_sector() returned an error.")); cut_assert_equal_int (13, mad_get_card_publisher_sector (mad), cut_message ("Wrong publisher sector")); res = mad_set_card_publisher_sector (mad, 0xff); cut_assert_equal_int (-1, res, cut_message ("Invalid sector")); cut_assert_equal_int (13, mad_get_card_publisher_sector (mad), cut_message ("Previous publisher sector value")); MadAid aid = { .function_cluster_code = 0, .application_code = 0 }; res = mad_get_aid (mad, 3, &aid); cut_assert_equal_int (0, res, cut_message ("mad_get_aid() failed")); cut_assert_equal_int (0, aid.function_cluster_code, cut_message ("Invalid default value")); cut_assert_equal_int (0, aid.application_code, cut_message ("Invalid default value")); aid.function_cluster_code = 0xc0; aid.application_code = 0x42; res = mad_set_aid (mad, 3, aid); cut_assert_equal_int (0, res, cut_message ("mad_set_aid() failed")); res = mad_get_aid (mad, 3, &aid); cut_assert_equal_int (0, res, cut_message ("mad_get_aid() failed")); cut_assert_equal_int (0xC0, aid.function_cluster_code, cut_message ("Invalid value")); cut_assert_equal_int (0x42, aid.application_code, cut_message ("Invalid value")); mad_free (mad); } #define CRC_PRESET 0xc7 void test_mad_crc8_basic (void) { uint8_t crc; const uint8_t crc_value = 0x42; /* Check integrity */ crc = CRC_PRESET; nxp_crc(&crc, crc_value); uint8_t save = crc; crc = CRC_PRESET; nxp_crc(&crc, crc_value); nxp_crc(&crc, save); cut_assert_equal_int (0x00, crc, cut_message ("CRC should verify crc(message + crc(message)) = 0")); } /* * The following MAD values where extracted from documentation. */ void test_mad_crc8_doc_example (void) { /* Preset */ uint8_t crc = CRC_PRESET; /* Block 1 -- 0x01 - 0x07 */ nxp_crc(&crc, 0x01); nxp_crc(&crc, 0x01); nxp_crc(&crc, 0x08); nxp_crc(&crc, 0x01); nxp_crc(&crc, 0x08); nxp_crc(&crc, 0x01); nxp_crc(&crc, 0x08); /* Block 2 -- 0x08 - 0x0f */ nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x04); nxp_crc(&crc, 0x00); /* Block 3 -- 0x00 - 0x07 */ nxp_crc(&crc, 0x03); nxp_crc(&crc, 0x10); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0x10); nxp_crc(&crc, 0x02); nxp_crc(&crc, 0x10); nxp_crc(&crc, 0x02); nxp_crc(&crc, 0x10); /* Block 3 -- 0x08 - 0x0f */ nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x11); nxp_crc(&crc, 0x30); /* Append zeros of augmented message */ cut_assert_equal_int (0x89, crc, cut_message ("Sample CRC should match")); } /* * The following MAD values where extracted from a MIFARE dump. */ void test_mad_crc8_real_example_1 (void) { /* Preset */ uint8_t crc = CRC_PRESET; /* Block 1 -- 0x01 - 0x07 */ nxp_crc(&crc, 0x01); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); /* Block 2 -- 0x08 - 0x0f */ nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); /* Block 3 -- 0x00 - 0x07 */ nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); /* Block 3 -- 0x08 - 0x0f */ nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); /* Append zeros of augmented message */ cut_assert_equal_int (0xc4, crc, cut_message ("Read example 1 CRC should match")); } /* * The following MAD values where extracted from a MIFARE dump. */ void test_mad_crc8_real_example_2 (void) { /* Preset */ uint8_t crc = CRC_PRESET; /* Block 1 -- 0x01 - 0x07 */ nxp_crc(&crc, 0x01); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); /* Block 2 -- 0x08 - 0x0f */ nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); nxp_crc(&crc, 0x03); nxp_crc(&crc, 0xe1); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); /* Block 3 -- 0x00 - 0x07 */ nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); /* Block 3 -- 0x08 - 0x0f */ nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); nxp_crc(&crc, 0x00); /* Append zeros of augmented message */ cut_assert_equal_int (0xab, crc, cut_message ("Read example 1 CRC should match")); } void test_mad_sector_0x00_crc8 (void) { int res; Mad mad = mad_new (1); cut_assert_not_null (mad, cut_message ("mad_new() failed")); res = mad_set_card_publisher_sector (mad, 0x01); /* Block 1 */ MadAid aid1 = { 0x01, 0x08 }; mad_set_aid (mad, 1, aid1); mad_set_aid (mad, 2, aid1); mad_set_aid (mad, 3, aid1); /* Block 2 */ MadAid empty_aid = { 0x00, 0x00 }; mad_set_aid (mad, 4, empty_aid); mad_set_aid (mad, 5, empty_aid); mad_set_aid (mad, 6, empty_aid); MadAid aid2 = { 0x04, 0x00 }; mad_set_aid (mad, 7, aid2); /* Block 3 */ MadAid aid3 = { 0x03, 0x10 }; mad_set_aid (mad, 8, aid3); mad_set_aid (mad, 9, aid3); MadAid aid4 = { 0x02, 0x10 }; mad_set_aid (mad, 10, aid4); mad_set_aid (mad, 11, aid4); mad_set_aid (mad, 12, empty_aid); mad_set_aid (mad, 13, empty_aid); mad_set_aid (mad, 14, empty_aid); MadAid aid5 = { 0x11, 0x30 }; mad_set_aid (mad, 15, aid5); res = sector_0x00_crc8 (mad); cut_assert_equal_int(0x89, res, cut_message ("Sample CRC should match")); mad_free (mad); } libfreefare-0.4.0/test/test_mifare_application.c000066400000000000000000000052041225640673100220000ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include void test_mifare_application (void) { /* Card publisher part */ MadAid aid = { 0x22, 0x42 }; Mad mad = mad_new (2); int i; cut_assert_not_null (mad, cut_message ("mad_new() failed")); MifareClassicSectorNumber *s_alloc = mifare_application_alloc (mad, aid, 3*3*16); cut_assert_not_null (s_alloc, cut_message ("mifare_application_alloc() failed")); MifareClassicSectorNumber *s_found = mifare_application_find (mad, aid); cut_assert_not_null (s_found, cut_message ("mifare_application_alloc() failed")); for (i = 0; s_alloc[i]; i++) { cut_assert_equal_int (s_alloc[i], s_found[i], cut_message ("Allocated and found blocks don't match at position %d", i)); } cut_assert_equal_int (0, s_alloc[i], cut_message ("Invalid size")); cut_assert_equal_int (0, s_found[i], cut_message ("Invalid size")); mifare_application_free (mad, aid); free (s_alloc); free (s_found); s_found = mifare_application_find (mad, aid); cut_assert_null (s_found, cut_message ("mifare_application_free() failed")); s_alloc = mifare_application_alloc (mad, aid, 15*16 + 1*16 + 1); cut_assert_not_null (s_alloc, cut_message ("mifare_application_alloc() failed")); s_found = mifare_application_find (mad, aid); cut_assert_not_null (s_found, cut_message ("mifare_application_alloc() failed")); for (i = 0; s_alloc[i]; i++) { cut_assert_equal_int (s_alloc[i], s_found[i], cut_message ("Allocated and found blocks don't match at position %d", i)); } cut_assert_equal_int (0, s_alloc[i], cut_message ("Invalid size")); cut_assert_equal_int (0, s_found[i], cut_message ("Invalid size")); mifare_application_free (mad, aid); free (s_alloc); free (s_found); s_found = mifare_application_find (mad, aid); cut_assert_null (s_found, cut_message ("mifare_application_free() failed")); mad_free (mad); } libfreefare-0.4.0/test/test_mifare_classic.c000066400000000000000000000451271225640673100211260ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include #include "freefare_internal.h" #include "mifare_classic_fixture.h" void test_mifare_classic_authenticate (void) { int res; MifareClassicKey k = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; res = mifare_classic_authenticate (tag, 0x00, k, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); } void test_mifare_classic_get_data_block_permission (void) { int res; MifareClassicKey k = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; res = mifare_classic_authenticate (tag, 0x04, k, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); cut_assert_equal_int (1, mifare_classic_get_data_block_permission(tag, 0x04, MCAB_R, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_data_block_permission(tag, 0x04, MCAB_R, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_data_block_permission(tag, 0x04, MCAB_W, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_data_block_permission(tag, 0x04, MCAB_W, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_data_block_permission(tag, 0x04, MCAB_D, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_data_block_permission(tag, 0x04, MCAB_D, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_data_block_permission(tag, 0x04, MCAB_I, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_data_block_permission(tag, 0x04, MCAB_I, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_READ_KEYA, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_READ_KEYA, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_WRITE_KEYA, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_WRITE_KEYA, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_READ_ACCESS_BITS, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_READ_ACCESS_BITS, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_WRITE_ACCESS_BITS, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_WRITE_ACCESS_BITS, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_READ_KEYB, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_READ_KEYB, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_WRITE_KEYB, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_trailer_block_permission(tag, 0x04, MCAB_WRITE_KEYB, MFC_KEY_B), cut_message ("Wrong permission")); } void test_mifare_classic_get_trailer_permission (void) { int res; MifareClassicKey k = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; res = mifare_classic_authenticate (tag, 0x07, k, MFC_KEY_A); cut_assert_equal_int (res, 0, cut_message ("mifare_classic_authenticate() failed")); cut_assert_equal_int (-1, mifare_classic_get_data_block_permission(tag, 0x07, MCAB_R, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_data_block_permission(tag, 0x07, MCAB_R, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_data_block_permission(tag, 0x07, MCAB_W, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_data_block_permission(tag, 0x07, MCAB_W, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_data_block_permission(tag, 0x07, MCAB_D, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_data_block_permission(tag, 0x07, MCAB_D, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_data_block_permission(tag, 0x07, MCAB_I, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (-1, mifare_classic_get_data_block_permission(tag, 0x07, MCAB_I, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (0, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_READ_KEYA, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (0, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_READ_KEYA, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_WRITE_KEYA, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (0, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_WRITE_KEYA, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_READ_ACCESS_BITS, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (0, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_READ_ACCESS_BITS, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_WRITE_ACCESS_BITS, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (0, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_WRITE_ACCESS_BITS, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_READ_KEYB, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (0, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_READ_KEYB, MFC_KEY_B), cut_message ("Wrong permission")); cut_assert_equal_int (1, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_WRITE_KEYB, MFC_KEY_A), cut_message ("Wrong permission")); cut_assert_equal_int (0, mifare_classic_get_trailer_block_permission(tag, 0x07, MCAB_WRITE_KEYB, MFC_KEY_B), cut_message ("Wrong permission")); } void test_mifare_classic_format_first_sector (void) { int res; MifareClassicKey k = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; res = mifare_classic_authenticate (tag, 0x00, k, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); MifareClassicBlock data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; MifareClassicBlock empty; memset (empty, '\x00', sizeof (empty)); MifareClassicBlock b0; res = mifare_classic_read (tag, 0x00, &b0); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); res = mifare_classic_write (tag, 0x00, data); cut_assert_equal_int (-1, res, cut_message ("mifare_classic_write() succeeded")); res = mifare_classic_disconnect (tag); res = mifare_classic_connect (tag); cut_assert_equal_int (0, res, cut_message ("mifare_classic_connect() failed")); res = mifare_classic_authenticate (tag, 0x00, k, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); res = mifare_classic_write (tag, 0x01, data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); res = mifare_classic_write (tag, 0x02, data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); res = mifare_classic_format_sector (tag, 0x00); cut_assert_equal_int (0, res, cut_message ("mifare_classic_format_sector() failed")); res = mifare_classic_read (tag, 0x00, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (data, sizeof (data), b0, sizeof (b0), cut_message ("Data changed in first block (block 1/3)")); res = mifare_classic_read (tag, 0x01, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (data, sizeof (data), empty, sizeof (data), cut_message ("Wrong data in formatted sector (block 2/3)")); res = mifare_classic_read (tag, 0x02, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (data, sizeof (data), empty, sizeof (data), cut_message ("Wrong data in formatted sector (block 3/3)")); res = mifare_classic_read (tag, 0x03, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (data, sizeof (data), "\x00\x00\x00\x00\x00\x00\xff\x07\x80\x69\xff\xff\xff\xff\xff\xff", sizeof (data), cut_message ("Wrong permissions in formatted sector")); } void test_mifare_classic_format (void) { int res; MifareClassicKey k = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; res = mifare_classic_authenticate (tag, 0x3c, k, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); MifareClassicBlock data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; MifareClassicBlock empty; memset (empty, 0, sizeof (empty)); res = mifare_classic_write (tag, 0x3c, data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); res = mifare_classic_write (tag, 0x3d, data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); res = mifare_classic_write (tag, 0x3e, data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); res = mifare_classic_format_sector (tag, mifare_classic_block_sector (0x3c)); cut_assert_equal_int (0, res, cut_message ("mifare_classic_format_sector() failed")); res = mifare_classic_read (tag, 0x3c, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (data, sizeof (data), empty, sizeof (data), cut_message ("Wrong data in formatted sector (block 1/3)")); res = mifare_classic_read (tag, 0x3d, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (data, sizeof (data), empty, sizeof (data), cut_message ("Wrong data in formatted sector (block 2/3)")); res = mifare_classic_read (tag, 0x3e, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (data, sizeof (data), empty, sizeof (data), cut_message ("Wrong data in formatted sector (block 3/3)")); res = mifare_classic_read (tag, 0x3f, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (data, sizeof (data), "\x00\x00\x00\x00\x00\x00\xff\x07\x80\x69\xff\xff\xff\xff\xff\xff", sizeof (data), cut_message ("Wrong permissions in formatted sector")); } void test_mifare_classic_value_block_increment (void) { int res; MifareClassicBlockNumber block = 0x04; MifareClassicKey k = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; res = mifare_classic_authenticate (tag, block, k, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); res = mifare_classic_init_value (tag, block, 1000, 0x00); cut_assert_equal_int (0, res, cut_message ("mifare_classic_init_value() failed")); /* Initialize value block */ int32_t value; MifareClassicBlockNumber adr; res = mifare_classic_read_value (tag, block, &value, &adr); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read_value() failed")); cut_assert_equal_int (1000, value, cut_message ("Wrong value block value")); cut_assert_equal_int (0x00, adr, cut_message ("Wrong value block address")); /* Increment by 1 */ res = mifare_classic_increment (tag, block, 1); cut_assert_equal_int (0, res, cut_message ("mifare_classic_increment() failed")); res = mifare_classic_transfer (tag, block); cut_assert_equal_int (0, res, cut_message ("mifare_classic_transfer() failed")); res = mifare_classic_read_value (tag, block, &value, &adr); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read_value() failed")); cut_assert_equal_int (1001, value, cut_message ("Wrong value block value")); cut_assert_equal_int (0x00, adr, cut_message ("Wrong value block address")); /* Increment by 10 */ res = mifare_classic_increment (tag, block, 10); cut_assert_equal_int (0, res, cut_message ("mifare_classic_increment() failed")); res = mifare_classic_transfer (tag, block); cut_assert_equal_int (0, res, cut_message ("mifare_classic_transfer() failed")); res = mifare_classic_read_value (tag, block, &value, &adr); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read_value() failed")); cut_assert_equal_int (1011, value, cut_message ("Wrong value block value")); cut_assert_equal_int (0x00, adr, cut_message ("Wrong value block address")); } void test_mifare_classic_value_block_decrement (void) { int res; MifareClassicBlockNumber block = 0x04; MifareClassicKey k = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; res = mifare_classic_authenticate (tag, block, k, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); res = mifare_classic_init_value (tag, block, 1000, 0x00); cut_assert_equal_int (0, res, cut_message ("mifare_classic_init_value() failed")); /* Initialize value block */ int32_t value; MifareClassicBlockNumber adr; res = mifare_classic_read_value (tag, block, &value, &adr); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read_value() failed")); cut_assert_equal_int (1000, value, cut_message ("Wrong value block value")); cut_assert_equal_int (0x00, adr, cut_message ("Wrong value block address")); /* Decrement */ res = mifare_classic_decrement (tag, block, 1); cut_assert_equal_int (0, res, cut_message ("mifare_classic_decrement() failed")); res = mifare_classic_transfer (tag, block); cut_assert_equal_int (0, res, cut_message ("mifare_classic_transfer() failed")); res = mifare_classic_read_value (tag, block, &value, &adr); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read_value() failed")); cut_assert_equal_int (999, value, cut_message ("Wrong value block value")); cut_assert_equal_int (0x00, adr, cut_message ("Wrong value block address")); res = mifare_classic_decrement (tag, block, 1000); cut_assert_equal_int (0, res, cut_message ("mifare_classic_decrement() failed")); res = mifare_classic_transfer (tag, block); cut_assert_equal_int (0, res, cut_message ("mifare_classic_transfer() failed")); res = mifare_classic_read_value (tag, block, &value, &adr); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read_value() failed")); cut_assert_equal_int (-1, value, cut_message ("Wrong value block value")); cut_assert_equal_int (0x00, adr, cut_message ("Wrong value block address")); } void test_mifare_classic_value_block_restore (void) { int res; MifareClassicBlockNumber block = 0x04; MifareClassicKey k = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; res = mifare_classic_authenticate (tag, block, k, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); /* Restore */ MifareClassicBlock data; MifareClassicBlock sample = { 0xe8, 0x03, 0x00, 0x00, 0x17, 0xfc, 0xff, 0xff, 0xe8, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff }; MifareClassicBlock nul = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff }; res = mifare_classic_write (tag, block, sample); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); res = mifare_classic_read (tag, block, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (sample, sizeof (sample), data, sizeof (data), cut_message ("Wrong value block contents")); res = mifare_classic_write (tag, block+1, nul); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); res = mifare_classic_read (tag, block+1, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (nul, sizeof (sample), data, sizeof (data), cut_message ("Wrong value block contents")); res = mifare_classic_restore (tag, block); cut_assert_equal_int (0, res, cut_message ("mifare_classic_restore() failed")); res = mifare_classic_transfer (tag, block+1); cut_assert_equal_int (0, res, cut_message ("mifare_classic_transfer() failed")); res = mifare_classic_read (tag, block+1, &data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_read() failed")); cut_assert_equal_memory (sample, sizeof (sample), data, sizeof (data), cut_message ("Wrong value block contents")); } void test_mifare_classic_get_uid (void) { char *uid; uid = freefare_get_tag_uid (tag); cut_assert_not_null (uid, cut_message ("freefare_get_tag_uid() failed")); cut_assert (((strlen (uid) == 8)||(strlen (uid) == 14)), cut_message ("Wrong UID length")); free (uid); } void test_mifare_classic_get_tag_friendly_name (void) { const char *name = freefare_get_tag_friendly_name (tag); cut_assert_not_null (name, cut_message ("freefare_get_tag_friendly_name() failed")); } libfreefare-0.4.0/test/test_mifare_classic_create_trailer_block.c000066400000000000000000000023531225640673100253370ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include void test_mifare_classic_create_trailer_block (void) { MifareClassicBlock data; MifareClassicKey key_a = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; MifareClassicKey key_b = { 0xde, 0xad, 0xbe, 0xef, 0xff, 0xff }; mifare_classic_trailer_block (&data, key_a, 0, 0, 0, 4, 0x42, key_b); cut_assert_equal_memory (data, sizeof (data), "\xff\xff\xff\xff\xff\xff\xff\x07\x80\x42\xde\xad\xbe\xef\xff\xff", sizeof (data), cut_message ("Wrong generated block")); } libfreefare-0.4.0/test/test_mifare_classic_mad.c000066400000000000000000000240461225640673100217440ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "mifare_classic_fixture.h" void test_mifare_classic_mad (void) { MifareClassicKey key_a_transport = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; MifareClassicKey key_b_sector_00 = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; MifareClassicKey key_b_sector_10 = { 0x1a, 0x98, 0x2c, 0x7e, 0x45 ,0x9a }; MifareClassicBlock tb; Mad mad; int res; /* __ __ _ ___ _ * | \/ | /_\ | \__ _/ | * | |\/| |/ _ \| |) \ V / | * |_| |_/_/ \_\___/ \_/|_| */ mad = mad_new (1); cut_assert_not_null (mad, cut_message ("mad_new() failed")); // Prepare sector 0x00 for writing a MAD. res = mifare_classic_authenticate (tag, 0x00, key_a_transport, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); mifare_classic_trailer_block (&tb, key_a_transport, 00, 00, 00, 06, 0x00, key_b_sector_00); res = mifare_classic_write (tag, 0x03, tb); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); // Write the empty MAD res = mad_write (tag, mad, key_b_sector_00, NULL); cut_assert_equal_int (0, res, cut_message ("mad_write() failed")); // Check the empty MAD MifareClassicBlock ref_01 = { 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; MifareClassicBlock ref_02 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; MifareClassicBlock data; res = mifare_classic_authenticate (tag, 0x01, mad_public_key_a, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); res = mifare_classic_read (tag, 0x01, &data); cut_assert_equal_int (0, res, cut_message ("mad_read() failed")); cut_assert_equal_memory (ref_01, sizeof (ref_01), data, sizeof (data), cut_message ("Wrong data")); res = mifare_classic_read (tag, 0x02, &data); cut_assert_equal_int (0, res, cut_message ("mad_read() failed")); cut_assert_equal_memory (ref_02, sizeof (ref_02), data, sizeof (data), cut_message ("Wrong data")); Mad mad2 = mad_read (tag); cut_assert_not_null (mad2, cut_message ("mad_read() failed")); cut_assert_equal_memory (mad, sizeof (mad), mad2, sizeof (mad2), cut_message ("Wrong MAD")); const char application_data[] = "APPLICATION DATA >> APPLICATION DATA >> APPLICATION DATA >> " \ "APPLICATION DATA >> APPLICATION DATA >> APPLICATION DATA >> " \ "APPLICATION DATA >> APPLICATION DATA >> APPLICATION DATA >> " \ "APPLICATION DATA >> APPLICATION DATA >> APPLICATION DATA >> " \ "APPLICATION DATA >> APPLICATION DATA >> APPLICATION DATA >> " \ "APPLICATION DATA >> APPLICATION DATA >> APPLICATION DATA >> "; MadAid aid = { .function_cluster_code = 0x01, .application_code = 0x12 }; // Write some data in the application MifareClassicSectorNumber *sectors = mifare_application_alloc (mad, aid, sizeof (application_data)); cut_assert_not_null (sectors, cut_message ("mifare_application_alloc() failed")); free (sectors); res = mad_write (tag, mad, key_b_sector_00, NULL); cut_assert_equal_int (0, res, cut_message ("mad_write() failed")); ssize_t s = mifare_application_write (tag, mad, aid, &application_data, sizeof (application_data), key_a_transport, MFC_KEY_A); cut_assert_equal_int (sizeof (application_data), s, cut_message ("mifare_application_write() failed")); char read_buf[500]; // Read it again s = mifare_application_read (tag, mad, aid, read_buf, sizeof (application_data), key_a_transport, MFC_KEY_A); cut_assert_equal_int (sizeof (application_data), s, cut_message ("mifare_application_read() failed")); cut_assert_equal_memory (application_data, sizeof (application_data), read_buf, s, cut_message ("Wrong application data")); mad_free (mad); mad_free (mad2); // Revert to the transport configuration res = mifare_classic_authenticate (tag, 0x00, key_b_sector_00, MFC_KEY_B); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); res = mifare_classic_format_sector (tag, 0x00); cut_assert_equal_int (0, res, cut_message ("mifare_classic_format_sector() failed")); /* __ __ _ ___ ___ * | \/ | /_\ | \__ _|_ ) * | |\/| |/ _ \| |) \ V // / * |_| |_/_/ \_\___/ \_//___| */ if (freefare_get_tag_type (tag) != CLASSIC_4K) { cut_omit ("MADv2 requires a MIFARE Classic 4K to be tested"); } mad = mad_new (2); cut_assert_not_null (mad, cut_message ("mad_new() failed")); // Prepare sector 0x00 for writing a MAD. res = mifare_classic_authenticate (tag, 0x00, key_a_transport, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); mifare_classic_trailer_block (&tb, key_a_transport, 00, 00, 00, 06, 0x00, key_b_sector_00); res = mifare_classic_write (tag, 0x03, tb); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); // Prepare sector 0x10 for writing a MAD. res = mifare_classic_authenticate (tag, 0x40, key_a_transport, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); mifare_classic_trailer_block (&tb, key_a_transport, 00, 00, 00, 06, 0x00, key_b_sector_10); res = mifare_classic_write (tag, 0x43, tb); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); // Write the empty MAD res = mad_write (tag, mad, key_b_sector_00, key_b_sector_10); cut_assert_equal_int (0, res, cut_message ("mad_write() failed")); // Check the empty MAD res = mifare_classic_authenticate (tag, 0x01, mad_public_key_a, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); res = mifare_classic_read (tag, 0x01, &data); cut_assert_equal_int (0, res, cut_message ("mad_read() failed")); cut_assert_equal_memory (ref_01, sizeof (ref_01), data, sizeof (data), cut_message ("Wrong data")); res = mifare_classic_read (tag, 0x02, &data); cut_assert_equal_int (0, res, cut_message ("mad_read() failed")); cut_assert_equal_memory (ref_02, sizeof (ref_02), data, sizeof (data), cut_message ("Wrong data")); MifareClassicBlock ref_40 = { 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; MifareClassicBlock ref_41 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; MifareClassicBlock ref_42 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; res = mifare_classic_authenticate (tag, 0x40, mad_public_key_a, MFC_KEY_A); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); res = mifare_classic_read (tag, 0x40, &data); cut_assert_equal_int (0, res, cut_message ("mad_read() failed")); cut_assert_equal_memory (ref_40, sizeof (ref_01), data, sizeof (data), cut_message ("Wrong data")); res = mifare_classic_read (tag, 0x41, &data); cut_assert_equal_int (0, res, cut_message ("mad_read() failed")); cut_assert_equal_memory (ref_41, sizeof (ref_02), data, sizeof (data), cut_message ("Wrong data")); res = mifare_classic_read (tag, 0x42, &data); cut_assert_equal_int (0, res, cut_message ("mad_read() failed")); cut_assert_equal_memory (ref_42, sizeof (ref_02), data, sizeof (data), cut_message ("Wrong data")); mad2 = mad_read (tag); cut_assert_not_null (mad2, cut_message ("mad_read() failed")); cut_assert_equal_memory (mad, sizeof (mad), mad2, sizeof (mad2), cut_message ("Wrong MAD")); // Write some data in the application sectors = mifare_application_alloc (mad, aid, sizeof (application_data)); cut_assert_not_null (sectors, cut_message ("mifare_application_alloc() failed")); free (sectors); res = mad_write (tag, mad, key_b_sector_00, key_b_sector_10); cut_assert_equal_int (0, res, cut_message ("mad_write() failed")); s = mifare_application_write (tag, mad, aid, &application_data, sizeof (application_data), key_a_transport, MFC_KEY_A); cut_assert_equal_int (sizeof (application_data), s, cut_message ("mifare_application_write() failed")); // Read it again s = mifare_application_read (tag, mad, aid, read_buf, sizeof (application_data), key_a_transport, MFC_KEY_A); cut_assert_equal_int (sizeof (application_data), s, cut_message ("mifare_application_read() failed")); cut_assert_equal_memory (application_data, sizeof (application_data), read_buf, s, cut_message ("Wrong application data")); mad_free (mad); mad_free (mad2); // Revert to the transport configuration res = mifare_classic_authenticate (tag, 0x00, key_b_sector_00, MFC_KEY_B); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); res = mifare_classic_format_sector (tag, 0x00); cut_assert_equal_int (0, res, cut_message ("mifare_classic_format_sector() failed")); res = mifare_classic_authenticate (tag, 0x40, key_b_sector_10, MFC_KEY_B); cut_assert_equal_int (0, res, cut_message ("mifare_classic_authenticate() failed")); res = mifare_classic_format_sector (tag, 0x10); cut_assert_equal_int (0, res, cut_message ("mifare_classic_format_sector() failed")); } libfreefare-0.4.0/test/test_mifare_classic_sector_boundaries.c000066400000000000000000000032651225640673100247150ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "freefare_internal.h" void test_mifare_classic_sector_boundaries (void) { for (int i=0; i < 32; i++) { for (int j=0; j < 4; j++) { cut_assert_equal_int (4 * i, mifare_classic_sector_first_block (mifare_classic_block_sector (4 * i)), cut_message ("Wrong first block number for block %d", i)); cut_assert_equal_int (4 * i + 3, mifare_classic_sector_last_block (mifare_classic_block_sector (4 * i + j)), cut_message ("Wrong last block number for block %d", i)); } } for (int i=0; i < 8; i++) { for (int j=0; j < 16; j++) { cut_assert_equal_int (128 + 16 * i, mifare_classic_sector_first_block (mifare_classic_block_sector (128 + 16 * i)), cut_message ("Wrong last block number for block %d", i)); cut_assert_equal_int (128 + 16 * i + 15, mifare_classic_sector_last_block (mifare_classic_block_sector (128 + 16 * i + j)), cut_message ("Wrong last block number for block %d", i)); } } } libfreefare-0.4.0/test/test_mifare_desfire.c000066400000000000000000001225531225640673100211250ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include #include #include "freefare_internal.h" #include "mifare_desfire_fixture.h" #include "common/mifare_desfire_auto_authenticate.h" #define cut_assert_success(last_command) \ do { \ cut_assert_equal_int (OPERATION_OK, mifare_desfire_last_picc_error (tag), cut_message ("PICC replied %s", mifare_desfire_error_lookup (mifare_desfire_last_picc_error (tag)))); \ cut_assert_not_equal_int (-1, res, cut_message ("Wrong return value")); \ } while (0) void test_mifare_desfire (void) { int res; /* Select the master application */ res = mifare_desfire_select_application (tag, NULL); cut_assert_success ("mifare_desfire_select_application()"); /* Get version information */ struct mifare_desfire_version_info version_info; res = mifare_desfire_get_version (tag, &version_info); cut_assert_success ("mifare_desfire_get_version()"); mifare_desfire_auto_authenticate (tag, 0); /* * This unit test change key settings to more restrictive ones, so reset * them to factory defaults in case the previous run failed unexpectedly. */ res = mifare_desfire_change_key_settings (tag, 0xF); cut_assert_success ("mifare_desfire_change_key_settings()"); /* Change master key to DES */ MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data_des); mifare_desfire_change_key (tag, 0, key, NULL); cut_assert_success ("mifare_desfire_change_key()"); uint8_t key_version; res = mifare_desfire_get_key_version (tag, 0, &key_version); cut_assert_success ("mifare_desfire_get_key_version()"); cut_assert_equal_int (0xAA, key_version, cut_message ("Wrong key_version value.")); res = mifare_desfire_authenticate (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate()"); mifare_desfire_key_free (key); /* Change master key to 3DES */ key = mifare_desfire_3des_key_new_with_version (key_data_3des); mifare_desfire_change_key (tag, 0, key, NULL); cut_assert_success ("mifare_desfire_change_key()"); res = mifare_desfire_get_key_version (tag, 0, &key_version); cut_assert_success ("mifare_desfire_get_key_version()"); cut_assert_equal_int (0xC7, key_version, cut_message ("Wrong key_version value.")); res = mifare_desfire_authenticate (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate()"); mifare_desfire_key_free (key); /* Wipeout the card */ res = mifare_desfire_format_picc (tag); cut_assert_success ("mifare_desfire_format_picc()"); /* Create 3 applications */ res = mifare_desfire_select_application (tag, NULL); cut_assert_success ("mifare_desfire_select_application()"); MifareDESFireAID aid_a = mifare_desfire_aid_new (0x00AAAAAA); cut_assert_not_null (aid_a, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application (tag, aid_a, 0xFF, 0); cut_assert_success ("mifare_desfire_create_application()"); MifareDESFireAID aid_b = mifare_desfire_aid_new (0x00BBBBBB); cut_assert_not_null (aid_b, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application (tag, aid_b, 0xEF, 6); cut_assert_success ("mifare_desfire_create_application()"); MifareDESFireAID aid_c = mifare_desfire_aid_new (0x00CCCCCC); cut_assert_not_null (aid_c, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application (tag, aid_c, 0xC2, 14); cut_assert_success ("mifare_desfire_create_application()"); // Ensure we can find the created applications MifareDESFireAID *aids = NULL; size_t aid_count; res = mifare_desfire_get_application_ids (tag, &aids, &aid_count); cut_assert_success ("mifare_desfire_get_application_ids()"); cut_assert_equal_int (3, aid_count, cut_message ("Wrong application count")); mifare_desfire_free_application_ids (aids); // Create files in the application A res = mifare_desfire_select_application (tag, aid_a); cut_assert_success ("mifare_desfire_select_application()"); uint8_t std_data_file_id = 15; res = mifare_desfire_create_std_data_file (tag, std_data_file_id, MDCM_PLAIN, 0xEEEE, 100); cut_assert_success ("mifare_desfire_create_std_data_file()"); res = mifare_desfire_create_backup_data_file (tag, 5, MDCM_PLAIN, 0xEEEE, 64); cut_assert_success ("mifare_desfire_create_backup_data_file()"); res = mifare_desfire_create_value_file (tag, 4, MDCM_PLAIN, 0xEEEE, 0, 1000, 0, 0); cut_assert_success ("mifare_desfire_create_value_file()"); res = mifare_desfire_create_cyclic_record_file (tag, 0, MDCM_PLAIN, 0xEEEE, 4, 10); cut_assert_success ("mifare_desfire_create_cyclic_record_file()"); // Write some data in the standard data file res = mifare_desfire_write_data (tag, std_data_file_id, 0, 30, (uint8_t *)"Some data to write to the card"); cut_assert_success ("mifare_desfire_write_data()"); cut_assert_equal_int (30, res, cut_message ("Wrong number of bytes writen")); res = mifare_desfire_write_data (tag, std_data_file_id, 34, 22, (uint8_t *)"Another block of data."); cut_assert_success ("mifare_desfire_write_data()"); cut_assert_equal_int (22, res, cut_message ("Wrong number of bytes writen")); // Make the file read-only res = mifare_desfire_change_file_settings (tag, std_data_file_id, MDCM_PLAIN, 0xEFFF); cut_assert_success ("mifare_desfire_change_file_settings()"); // Read a part of the file uint8_t buffer[120]; res = mifare_desfire_read_data (tag, std_data_file_id, 10, 50, &buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (50, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory ("to write to the card\0\0\0\0Another block of data.\0\0\0\0", 50, buffer, 50, cut_message ("Wrong data")); // Read all the file at once res = mifare_desfire_read_data (tag, std_data_file_id, 0, 0, &buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (100, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory ("Some data to write to the" " card\0\0\0\0Another block of" " data.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 100, buffer, 100, cut_message ("Wrong data")); // Try to overwrute the file res = mifare_desfire_write_data (tag, std_data_file_id, 20, 5, (char *)"Test!"); cut_assert_equal_int (-1, res, cut_message ("Wrong return value")); cut_assert_equal_int (PERMISSION_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Wrong PICC error")); int32_t expected_value = 0; for (int transaction = 0; transaction < 15; transaction++) { char data_buffer[3]; sprintf (data_buffer, "%02d", transaction); // Write to the backup file res = mifare_desfire_write_data (tag, 5, 3*transaction, 3, data_buffer); cut_assert_success ("mifare_desfire_write_data()"); // Manipulate the value file res = mifare_desfire_credit (tag, 4, 100); cut_assert_success ("mifare_desfire_credit()"); res = mifare_desfire_debit (tag, 4, 97); cut_assert_success ("mifare_desfire_debit()"); // Write to the cyclic record file res = mifare_desfire_write_record (tag, 0, 2, 2, data_buffer); cut_assert_success ("mifare_desfire_write_record()"); // Overwrite the cyclic record file res = mifare_desfire_write_record (tag, 0, 0, 2, (char *)"r."); cut_assert_success("mifare_desfire_write_record()"); // Ensure that no content was changed yet char ref_buffer[64]; memset (ref_buffer, 0, sizeof (ref_buffer)); for (int n = 0; n < transaction; n++) { sprintf (ref_buffer + 3 * n, "%02d", n); } res = mifare_desfire_read_data (tag, 5, 0, 0, buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (64, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory (buffer, 64, ref_buffer, 64, cut_message ("Wrong data")); int32_t value; res = mifare_desfire_get_value (tag, 4, &value); cut_assert_success ("mifare_desfire_get_value()"); cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); // Reading records from an empty file would abort the transaction if (0 != transaction) { // Get the latest record res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); cut_assert_success ("mifare_desfire_read_records()"); sprintf (ref_buffer, "r.%02d", transaction); cut_assert_not_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); } // Commit ! res = mifare_desfire_commit_transaction (tag); cut_assert_success ("mifare_desfire_commit_transaction()"); res = mifare_desfire_read_data (tag, 5, 3*transaction, 3, buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_memory (data_buffer, 3, buffer, res, cut_message ("Wrong data")); expected_value += 3; res = mifare_desfire_get_value (tag, 4, &value); cut_assert_success ("mifare_desfire_get_value()"); cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); cut_assert_success ("mifare_desfire_read_records()"); sprintf (ref_buffer, "r.%02d", transaction); cut_assert_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); } // Ensure limited credit is disabled res = mifare_desfire_limited_credit (tag, 4, 20); cut_assert_equal_int (-1, res, cut_message ("mifare_desfire_limited_credit() should fail")); // Get all files uint8_t *files; size_t file_count; res = mifare_desfire_get_file_ids (tag, &files, &file_count); cut_assert_success ("mifare_desfire_get_file_ids()"); cut_assert_equal_int (4, file_count, cut_message ("Wrong number of files")); for (size_t i=0; i * * $Id$ */ /* * These tests implement examples provided in the following documents: * * NIST Special Publication 800-38B * Recommendation for Block Cipher Modes of Operation: The CMAC Mode for Authentication * May 2005 */ #include #include #include #include "freefare_internal.h" uint8_t key_data[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; void test_mifare_desfire_aes_generate_subkeys (void) { uint8_t sk1[] = { 0xfb, 0xee, 0xd6, 0x18, 0x35, 0x71, 0x33, 0x66, 0x7c, 0x85, 0xe0, 0x8f, 0x72, 0x36, 0xa8, 0xde }; uint8_t sk2[] = { 0xf7, 0xdd, 0xac, 0x30, 0x6a, 0xe2, 0x66, 0xcc, 0xf9, 0x0b, 0xc1, 0x1e, 0xe4, 0x6d, 0x51, 0x3b }; MifareDESFireKey key = mifare_desfire_aes_key_new (key_data); cmac_generate_subkeys (key); cut_assert_equal_memory (sk1, 16, key->cmac_sk1, 16, cut_message ("Wrong sub-key 1")); cut_assert_equal_memory (sk2, 16, key->cmac_sk2, 16, cut_message ("Wrong sub-key 2")); mifare_desfire_key_free (key); } void test_mifare_desfire_aes_cmac_empty (void) { MifareDESFireKey key = mifare_desfire_aes_key_new (key_data); cmac_generate_subkeys (key); uint8_t ivect[16]; memset (ivect, 0, sizeof (ivect)); uint8_t expected_cmac[] = { 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 }; uint8_t my_cmac[16]; cmac (key, ivect, NULL, 0, my_cmac); cut_assert_equal_memory (expected_cmac, 16, my_cmac, 16, cut_message ("Wrong CMAC")); mifare_desfire_key_free (key); } void test_mifare_desfire_aes_cmac_128 (void) { MifareDESFireKey key = mifare_desfire_aes_key_new (key_data); cmac_generate_subkeys (key); uint8_t ivect[16]; memset (ivect, 0, sizeof (ivect)); uint8_t message[] = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a }; uint8_t expected_cmac[] = { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c }; uint8_t my_cmac[16]; cmac (key, ivect, message, 16, my_cmac); cut_assert_equal_memory (expected_cmac, 16, my_cmac, sizeof (message), cut_message ("Wrong CMAC")); mifare_desfire_key_free (key); } void test_mifare_desfire_aes_cmac_320 (void) { MifareDESFireKey key = mifare_desfire_aes_key_new (key_data); cmac_generate_subkeys (key); uint8_t ivect[16]; memset (ivect, 0, sizeof (ivect)); uint8_t message[] = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 }; uint8_t expected_cmac[] = { 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 }; uint8_t my_cmac[16]; cmac (key, ivect, message, sizeof (message), my_cmac); cut_assert_equal_memory (expected_cmac, 16, my_cmac, 16, cut_message ("Wrong CMAC")); mifare_desfire_key_free (key); } void test_mifare_desfire_aes_cmac_512 (void) { MifareDESFireKey key = mifare_desfire_aes_key_new (key_data); cmac_generate_subkeys (key); uint8_t ivect[16]; memset (ivect, 0, sizeof (ivect)); uint8_t message[] = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }; uint8_t expected_cmac[] = { 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe }; uint8_t my_cmac[16]; cmac (key, ivect, message, sizeof (message), my_cmac); cut_assert_equal_memory (expected_cmac, 16, my_cmac, 16, cut_message ("Wrong CMAC")); mifare_desfire_key_free (key); } libfreefare-0.4.0/test/test_mifare_desfire_aid.c000066400000000000000000000036271225640673100217420ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "freefare_internal.h" void test_mifare_desfire_aid (void) { /* * <-- LSB MSB --> * | MIFARE DESFire AID Byte 0 | MIFARE DESFire AID Byte 1 | MIFARE DESFire AID Byte 2 | * | Nible 0 | Nible 1 | Nible 2 | Nible 3 | Nible 4 | Nible 5 | * | 0xF | MIFARE Classic AID | 0x0...0xF | * | Function-Cluster | Application code | * <-- MSB LSB--> * * 0xF21438 -> 0x83412F */ MifareDESFireAID desfire_aid = mifare_desfire_aid_new (0x00f12ab8); MadAid mad_aid = { .function_cluster_code = 0x12, .application_code = 0xab, }; MifareDESFireAID desfire_aid2 = mifare_desfire_aid_new_with_mad_aid (mad_aid, 8); cut_assert_equal_memory (desfire_aid->data,3, desfire_aid2->data, 3, cut_message ("wrong aid")); cut_assert_equal_int (mifare_desfire_aid_get_aid (desfire_aid), 0x00f12ab8, cut_message ("wrong aid")); free (desfire_aid); free (desfire_aid2); } libfreefare-0.4.0/test/test_mifare_desfire_des.c000066400000000000000000000065331225640673100217570ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include "freefare_internal.h" void test_mifare_rol (void) { uint8_t data[8] = "01234567"; rol (data, 8); cut_assert_equal_memory ("12345670", 8, data, 8, cut_message ("Wrong data")); uint8_t data2[16] = "0123456789abcdef"; rol (data2, 16); cut_assert_equal_memory (data2, 16, "123456789abcdef0", 16, cut_message ("Wrong data")); } void test_mifare_desfire_des_receive (void) { uint8_t null_ivect[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t data[8] = { 0xd6, 0x59, 0xe1, 0x70, 0x43, 0xa8, 0x40, 0x68 }; uint8_t key_data[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data); uint8_t expected_data[8] = { 0x73, 0x0d, 0xdf, 0xad, 0xa4, 0xd2, 0x07, 0x89 }; uint8_t expected_key[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; mifare_cypher_blocks_chained (NULL, key, null_ivect, data, 8, MCD_RECEIVE, MCO_DECYPHER); cut_assert_equal_memory (&expected_data, 8, &data, 8, cut_message ("Wrong data")); cut_assert_equal_memory (&expected_key, 8, key->data, 8, cut_message ("Wrong key")); mifare_desfire_key_free (key); } void test_mifare_desfire_des_send (void) { uint8_t null_ivect[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t data[8] = { 0x73, 0x0d, 0xdf, 0xad, 0xa4, 0xd2, 0x07, 0x89 }; uint8_t key_data[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data); uint8_t expected_data[8] = { 0xd6, 0x59, 0xe1, 0x70, 0x43, 0xa8, 0x40, 0x68 }; uint8_t expected_key[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; mifare_cypher_blocks_chained (NULL, key, null_ivect, data, 8, MCD_SEND, MCO_DECYPHER); cut_assert_equal_memory (&expected_data, 8, &data, 8, cut_message ("Wrong data")); cut_assert_equal_memory (&expected_key, 8, key->data, 8, cut_message ("Wrong key")); mifare_desfire_key_free (key); } void test_mifare_desfire_padded_data_length (void) { size_t res; res = padded_data_length (0, 8); cut_assert_equal_int (res, 8, cut_message ("Invalid size")); res = padded_data_length (1, 8); cut_assert_equal_int (res, 8, cut_message ("Invalid size")); res = padded_data_length (8, 8); cut_assert_equal_int (res, 8, cut_message ("Invalid size")); res = padded_data_length (9, 8); cut_assert_equal_int (res, 16, cut_message ("Invalid size")); res = padded_data_length (0, 16); cut_assert_equal_int (res, 16, cut_message ("Invalid size")); res = padded_data_length (33, 16); cut_assert_equal_int (res, 48, cut_message ("Invalid size")); } libfreefare-0.4.0/test/test_mifare_desfire_ev1.c000066400000000000000000000136261225640673100217000ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include #include #include "freefare_internal.h" #include "mifare_desfire_ev1_fixture.h" #include "common/mifare_desfire_auto_authenticate.h" #define cut_assert_success(last_command) \ do { \ cut_assert_equal_int (OPERATION_OK, mifare_desfire_last_picc_error (tag), cut_message ("PICC replied %s", mifare_desfire_error_lookup (mifare_desfire_last_picc_error (tag)))); \ cut_assert_not_equal_int (-1, res, cut_message ("Wrong return value")); \ } while (0) void test_mifare_desfire_ev1_aes2 (void) { int res; MifareDESFireKey key; mifare_desfire_auto_authenticate (tag, 0); // Setup the AES key key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version); res = mifare_desfire_change_key (tag, 0x80, key, NULL); cut_assert_success ("mifare_desfire_change_key"); mifare_desfire_key_free (key); // Authenticate with the AES key key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version); res = mifare_desfire_authenticate_aes (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate"); mifare_desfire_key_free (key); res = mifare_desfire_format_picc (tag); cut_assert_success ("mifare_desfire_format_picc()"); key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version); res = mifare_desfire_authenticate_aes (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate"); mifare_desfire_key_free (key); uint32_t size; res = mifare_desfire_free_mem (tag, &size); cut_assert_success ("mifare_desfire_free_mem"); // Do some commands to check CMAC is properly handled res = mifare_desfire_free_mem (tag, &size); cut_assert_success ("mifare_desfire_free_mem"); struct mifare_desfire_version_info info; res = mifare_desfire_get_version (tag, &info); cut_assert_success ("mifare_desfire_get_version"); res = mifare_desfire_change_key_settings (tag, 0x0F); cut_assert_success ("mifare_desfire_change_key_settings"); res = mifare_desfire_free_mem (tag, &size); cut_assert_success ("mifare_desfire_free_mem"); MifareDESFireAID aid = mifare_desfire_aid_new (0x112233); mifare_desfire_delete_application (tag, aid); res = mifare_desfire_create_application (tag, aid, 0xff, 0x81); cut_assert_success ("mifare_desfire_create_application"); res = mifare_desfire_select_application (tag, aid); cut_assert_success ("mifare_desfire_select_application"); key = mifare_desfire_aes_key_new (key_data_aes); res = mifare_desfire_authenticate_aes (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate"); free (key); key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version); res = mifare_desfire_change_key (tag, 0x00, key, NULL); cut_assert_success ("mifare_desfire_change_key"); mifare_desfire_key_free (key); key = mifare_desfire_aes_key_new (key_data_aes); res = mifare_desfire_authenticate_aes (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate"); free (key); res = mifare_desfire_create_std_data_file (tag, 1, MDCM_MACED, 0x0000, 512); if ((mifare_desfire_last_picc_error (tag) != DUPLICATE_ERROR) && (mifare_desfire_last_picc_error(tag) != OPERATION_OK)) cut_assert_success ("mifare_desfire_create_std_data_file"); char sample_data[] = "Hello World! I'm a string that is probably too long " "to feet in a single frame. For this reason, it will be split and like" "ly, some failure in the algorirthm should trigger an error in this uni" "t test."; res = mifare_desfire_write_data_ex (tag, 1, 0, strlen (sample_data), sample_data, MDCM_MACED); cut_assert_success ("mifare_desfire_write_data"); char buffer[1024]; res = mifare_desfire_read_data_ex (tag, 1, 0, 27, buffer, MDCM_MACED); cut_assert_success ("mifare_desfire_read_data"); cut_assert_equal_memory (buffer, res, sample_data, 27, cut_message ("AES crypto failed")); uint8_t s, c; res = mifare_desfire_get_key_settings (tag, &s, &c); cut_assert_success ("mifare_desfire_get__key_settings"); res = mifare_desfire_read_data_ex (tag, 1, 27, 27, buffer, MDCM_MACED); cut_assert_success ("mifare_desfire_read_data"); cut_assert_equal_memory (buffer, res, sample_data + 27, 27, cut_message ("AES crypto failed")); res = mifare_desfire_read_data_ex (tag, 1, 0, 0, buffer, MDCM_MACED); cut_assert_success ("mifare_desfire_read_data"); cut_assert_equal_memory (buffer, strlen (buffer), sample_data, strlen (sample_data), cut_message ("AES crypto failed")); // Revert to the default DES key res = mifare_desfire_select_application (tag, NULL); cut_assert_success ("mifare_desfire_select_application"); key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version); res = mifare_desfire_authenticate_aes (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate"); mifare_desfire_key_free (key); key = mifare_desfire_des_key_new (key_data_null); res = mifare_desfire_change_key (tag, 0x00, key, NULL); cut_assert_success ("mifare_desfire_change_key"); mifare_desfire_key_free (key); } libfreefare-0.4.0/test/test_mifare_desfire_ev1_3des.c000066400000000000000000001066471225640673100226240ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include #include #include "freefare_internal.h" #include "mifare_desfire_ev1_fixture.h" #include "common/mifare_desfire_auto_authenticate.h" #define cut_assert_success(last_command) \ do { \ cut_assert_equal_int (OPERATION_OK, mifare_desfire_last_picc_error (tag), cut_message ("PICC replied %s", mifare_desfire_error_lookup (mifare_desfire_last_picc_error (tag)))); \ cut_assert_not_equal_int (-1, res, cut_message ("Wrong return value")); \ } while (0) void test_mifare_desfire_ev1_3des (void) { int res; /* Select the master application */ res = mifare_desfire_select_application (tag, NULL); cut_assert_success ("mifare_desfire_select_application()"); /* Get version information */ struct mifare_desfire_version_info version_info; res = mifare_desfire_get_version (tag, &version_info); cut_assert_success ("mifare_desfire_get_version()"); mifare_desfire_auto_authenticate (tag, 0); /* * This unit test change key settings to more restrictive ones, so reset * them to factory defaults in case the previous run failed unexpectedly. */ res = mifare_desfire_change_key_settings (tag, 0xF); cut_assert_success ("mifare_desfire_change_key_settings()"); res = mifare_desfire_change_key_settings (tag, 0xF); cut_assert_success ("mifare_desfire_change_key_settings()"); /* Change master key to AES */ MifareDESFireKey key = mifare_desfire_3des_key_new_with_version (key_data_3des); mifare_desfire_change_key (tag, 0, key, NULL); cut_assert_success ("mifare_desfire_change_key()"); res = mifare_desfire_authenticate_iso (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate_iso()"); mifare_desfire_key_free (key); /* Wipeout the card */ res = mifare_desfire_format_picc (tag); cut_assert_success ("mifare_desfire_format_picc()"); /* Create 3 applications */ res = mifare_desfire_select_application (tag, NULL); cut_assert_success ("mifare_desfire_select_application()"); key = mifare_desfire_3des_key_new_with_version (key_data_3des); res = mifare_desfire_authenticate_iso (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate_iso()"); mifare_desfire_key_free (key); uint8_t key_version; res = mifare_desfire_get_key_version (tag, 0, &key_version); cut_assert_success ("mifare_desfire_get_key_version()"); cut_assert_equal_int (0x55, 0x55, cut_message ("Wrong key_version value.")); uint32_t size; res = mifare_desfire_free_mem (tag, &size); cut_assert_success ("mifare_desfire_free_mem()"); MifareDESFireAID aid_a = mifare_desfire_aid_new (0x00AAAAAA); //cut_assert_not_null (aid_a, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application (tag, aid_a, 0xFF, 0); cut_assert_success ("mifare_desfire_create_application()"); MifareDESFireAID aid_b = mifare_desfire_aid_new (0x00BBBBBB); cut_assert_not_null (aid_b, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application (tag, aid_b, 0xEF, 6); cut_assert_success ("mifare_desfire_create_application()"); MifareDESFireAID aid_c = mifare_desfire_aid_new (0x00CCCCCC); cut_assert_not_null (aid_c, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application (tag, aid_c, 0xC2, 14); cut_assert_success ("mifare_desfire_create_application()"); // Ensure we can find the created applications MifareDESFireAID *aids = NULL; size_t aid_count; res = mifare_desfire_get_application_ids (tag, &aids, &aid_count); cut_assert_success ("mifare_desfire_get_application_ids()"); cut_assert_equal_int (3, aid_count, cut_message ("Wrong application count")); mifare_desfire_free_application_ids (aids); // Create files in the application A res = mifare_desfire_select_application (tag, aid_a); cut_assert_success ("mifare_desfire_select_application()"); uint8_t std_data_file_id = 15; res = mifare_desfire_create_std_data_file (tag, std_data_file_id, MDCM_PLAIN, 0xEEEE, 100); cut_assert_success ("mifare_desfire_create_std_data_file()"); res = mifare_desfire_create_backup_data_file (tag, 5, MDCM_PLAIN, 0xEEEE, 64); cut_assert_success ("mifare_desfire_create_backup_data_file()"); res = mifare_desfire_create_value_file (tag, 4, MDCM_PLAIN, 0xEEEE, 0, 1000, 0, 0); cut_assert_success ("mifare_desfire_create_value_file()"); res = mifare_desfire_create_cyclic_record_file (tag, 0, MDCM_PLAIN, 0xEEEE, 4, 10); cut_assert_success ("mifare_desfire_create_cyclic_record_file()"); // Write some data in the standard data file res = mifare_desfire_write_data (tag, std_data_file_id, 0, 30, (uint8_t *)"Some data to write to the card"); cut_assert_success ("mifare_desfire_write_data()"); cut_assert_equal_int (30, res, cut_message ("Wrong number of bytes writen")); res = mifare_desfire_write_data (tag, std_data_file_id, 34, 22, (uint8_t *)"Another block of data."); cut_assert_success ("mifare_desfire_write_data()"); cut_assert_equal_int (22, res, cut_message ("Wrong number of bytes writen")); // Make the file read-only res = mifare_desfire_change_file_settings (tag, std_data_file_id, MDCM_PLAIN, 0xEFFF); cut_assert_success ("mifare_desfire_change_file_settings()"); // Read a part of the file uint8_t buffer[120]; res = mifare_desfire_read_data (tag, std_data_file_id, 10, 50, &buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (50, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory ("to write to the card\0\0\0\0Another block of data.\0\0\0\0", 50, buffer, 50, cut_message ("Wrong data")); // Read all the file at once res = mifare_desfire_read_data (tag, std_data_file_id, 0, 0, &buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (100, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory ("Some data to write to the" " card\0\0\0\0Another block of" " data.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 100, buffer, 100, cut_message ("Wrong data")); // Try to overwrute the file res = mifare_desfire_write_data (tag, std_data_file_id, 20, 5, (char *)"Test!"); cut_assert_equal_int (-1, res, cut_message ("Wrong return value")); cut_assert_equal_int (PERMISSION_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Wrong PICC error")); int32_t expected_value = 0; for (int transaction = 0; transaction < 15; transaction++) { char data_buffer[3]; sprintf (data_buffer, "%02d", transaction); // Write to the backup file res = mifare_desfire_write_data (tag, 5, 3*transaction, 3, data_buffer); cut_assert_success ("mifare_desfire_write_data()"); // Manipulate the value file res = mifare_desfire_credit (tag, 4, 100); cut_assert_success ("mifare_desfire_credit()"); res = mifare_desfire_debit (tag, 4, 97); cut_assert_success ("mifare_desfire_debit()"); // Write to the cyclic record file res = mifare_desfire_write_record (tag, 0, 2, 2, data_buffer); cut_assert_success ("mifare_desfire_write_record()"); // Overwrite the cyclic record file res = mifare_desfire_write_record (tag, 0, 0, 2, (char *)"r."); cut_assert_success("mifare_desfire_write_record()"); // Ensure that no content was changed yet char ref_buffer[64]; memset (ref_buffer, 0, sizeof (ref_buffer)); for (int n = 0; n < transaction; n++) { sprintf (ref_buffer + 3 * n, "%02d", n); } res = mifare_desfire_read_data (tag, 5, 0, 0, buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (64, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory (buffer, 64, ref_buffer, 64, cut_message ("Wrong data")); int32_t value; res = mifare_desfire_get_value (tag, 4, &value); cut_assert_success ("mifare_desfire_get_value()"); cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); // Reading records from an empty file would abort the transaction if (0 != transaction) { // Get the latest record res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); cut_assert_success ("mifare_desfire_read_records()"); sprintf (ref_buffer, "r.%02d", transaction); cut_assert_not_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); } // Commit ! res = mifare_desfire_commit_transaction (tag); cut_assert_success ("mifare_desfire_commit_transaction()"); res = mifare_desfire_read_data (tag, 5, 3*transaction, 3, buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_memory (data_buffer, 3, buffer, res, cut_message ("Wrong data")); expected_value += 3; res = mifare_desfire_get_value (tag, 4, &value); cut_assert_success ("mifare_desfire_get_value()"); cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); cut_assert_success ("mifare_desfire_read_records()"); sprintf (ref_buffer, "r.%02d", transaction); cut_assert_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); } // Ensure limited credit is disabled res = mifare_desfire_limited_credit (tag, 4, 20); cut_assert_equal_int (-1, res, cut_message ("mifare_desfire_limited_credit() should fail")); // Get all files uint8_t *files; size_t file_count; res = mifare_desfire_get_file_ids (tag, &files, &file_count); cut_assert_success ("mifare_desfire_get_file_ids()"); cut_assert_equal_int (4, file_count, cut_message ("Wrong number of files")); for (size_t i=0; i * * $Id$ */ #include #include #include #include #include #include "freefare_internal.h" #include "mifare_desfire_ev1_fixture.h" #include "common/mifare_desfire_auto_authenticate.h" #define cut_assert_success(last_command) \ do { \ cut_assert_equal_int (OPERATION_OK, mifare_desfire_last_picc_error (tag), cut_message ("PICC replied %s", mifare_desfire_error_lookup (mifare_desfire_last_picc_error (tag)))); \ cut_assert_not_equal_int (-1, res, cut_message ("Wrong return value")); \ } while (0) void test_mifare_desfire_ev1_3k3des (void) { int res; /* Select the master application */ res = mifare_desfire_select_application (tag, NULL); cut_assert_success ("mifare_desfire_select_application()"); /* Get version information */ struct mifare_desfire_version_info version_info; res = mifare_desfire_get_version (tag, &version_info); cut_assert_success ("mifare_desfire_get_version()"); mifare_desfire_auto_authenticate (tag, 0); /* * This unit test change key settings to more restrictive ones, so reset * them to factory defaults in case the previous run failed unexpectedly. */ res = mifare_desfire_change_key_settings (tag, 0xF); cut_assert_success ("mifare_desfire_change_key_settings()"); res = mifare_desfire_change_key_settings (tag, 0xF); cut_assert_success ("mifare_desfire_change_key_settings()"); /* Change master key to AES */ MifareDESFireKey key = mifare_desfire_3k3des_key_new_with_version (key_data_3k3des); mifare_desfire_change_key (tag, 0, key, NULL); cut_assert_success ("mifare_desfire_change_key()"); res = mifare_desfire_authenticate_iso (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate_iso()"); mifare_desfire_key_free (key); /* Wipeout the card */ res = mifare_desfire_format_picc (tag); cut_assert_success ("mifare_desfire_format_picc()"); /* Create 3 applications */ res = mifare_desfire_select_application (tag, NULL); cut_assert_success ("mifare_desfire_select_application()"); key = mifare_desfire_3k3des_key_new_with_version (key_data_3k3des); res = mifare_desfire_authenticate_iso (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate_iso()"); mifare_desfire_key_free (key); uint8_t key_version; res = mifare_desfire_get_key_version (tag, 0, &key_version); cut_assert_success ("mifare_desfire_get_key_version()"); cut_assert_equal_int (0x55, 0x55, cut_message ("Wrong key_version value.")); uint32_t size; res = mifare_desfire_free_mem (tag, &size); cut_assert_success ("mifare_desfire_free_mem()"); MifareDESFireAID aid_a = mifare_desfire_aid_new (0x00AAAAAA); //cut_assert_not_null (aid_a, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application (tag, aid_a, 0xFF, 0); cut_assert_success ("mifare_desfire_create_application()"); MifareDESFireAID aid_b = mifare_desfire_aid_new (0x00BBBBBB); cut_assert_not_null (aid_b, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application_3k3des (tag, aid_b, 0xEF, 6); cut_assert_success ("mifare_desfire_create_application()"); MifareDESFireAID aid_c = mifare_desfire_aid_new (0x00CCCCCC); cut_assert_not_null (aid_c, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application_3k3des (tag, aid_c, 0xC2, 14); cut_assert_success ("mifare_desfire_create_application()"); // Ensure we can find the created applications MifareDESFireAID *aids = NULL; size_t aid_count; res = mifare_desfire_get_application_ids (tag, &aids, &aid_count); cut_assert_success ("mifare_desfire_get_application_ids()"); cut_assert_equal_int (3, aid_count, cut_message ("Wrong application count")); mifare_desfire_free_application_ids (aids); // Create files in the application A res = mifare_desfire_select_application (tag, aid_a); cut_assert_success ("mifare_desfire_select_application()"); uint8_t std_data_file_id = 15; res = mifare_desfire_create_std_data_file (tag, std_data_file_id, MDCM_PLAIN, 0xEEEE, 100); cut_assert_success ("mifare_desfire_create_std_data_file()"); res = mifare_desfire_create_backup_data_file (tag, 5, MDCM_PLAIN, 0xEEEE, 64); cut_assert_success ("mifare_desfire_create_backup_data_file()"); res = mifare_desfire_create_value_file (tag, 4, MDCM_PLAIN, 0xEEEE, 0, 1000, 0, 0); cut_assert_success ("mifare_desfire_create_value_file()"); res = mifare_desfire_create_cyclic_record_file (tag, 0, MDCM_PLAIN, 0xEEEE, 4, 10); cut_assert_success ("mifare_desfire_create_cyclic_record_file()"); // Write some data in the standard data file res = mifare_desfire_write_data (tag, std_data_file_id, 0, 30, (uint8_t *)"Some data to write to the card"); cut_assert_success ("mifare_desfire_write_data()"); cut_assert_equal_int (30, res, cut_message ("Wrong number of bytes writen")); res = mifare_desfire_write_data (tag, std_data_file_id, 34, 22, (uint8_t *)"Another block of data."); cut_assert_success ("mifare_desfire_write_data()"); cut_assert_equal_int (22, res, cut_message ("Wrong number of bytes writen")); // Make the file read-only res = mifare_desfire_change_file_settings (tag, std_data_file_id, MDCM_PLAIN, 0xEFFF); cut_assert_success ("mifare_desfire_change_file_settings()"); // Read a part of the file uint8_t buffer[120]; res = mifare_desfire_read_data (tag, std_data_file_id, 10, 50, &buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (50, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory ("to write to the card\0\0\0\0Another block of data.\0\0\0\0", 50, buffer, 50, cut_message ("Wrong data")); // Read all the file at once res = mifare_desfire_read_data (tag, std_data_file_id, 0, 0, &buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (100, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory ("Some data to write to the" " card\0\0\0\0Another block of" " data.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 100, buffer, 100, cut_message ("Wrong data")); // Try to overwrute the file res = mifare_desfire_write_data (tag, std_data_file_id, 20, 5, (char *)"Test!"); cut_assert_equal_int (-1, res, cut_message ("Wrong return value")); cut_assert_equal_int (PERMISSION_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Wrong PICC error")); int32_t expected_value = 0; for (int transaction = 0; transaction < 15; transaction++) { char data_buffer[3]; sprintf (data_buffer, "%02d", transaction); // Write to the backup file res = mifare_desfire_write_data (tag, 5, 3*transaction, 3, data_buffer); cut_assert_success ("mifare_desfire_write_data()"); // Manipulate the value file res = mifare_desfire_credit (tag, 4, 100); cut_assert_success ("mifare_desfire_credit()"); res = mifare_desfire_debit (tag, 4, 97); cut_assert_success ("mifare_desfire_debit()"); // Write to the cyclic record file res = mifare_desfire_write_record (tag, 0, 2, 2, data_buffer); cut_assert_success ("mifare_desfire_write_record()"); // Overwrite the cyclic record file res = mifare_desfire_write_record (tag, 0, 0, 2, (char *)"r."); cut_assert_success("mifare_desfire_write_record()"); // Ensure that no content was changed yet char ref_buffer[64]; memset (ref_buffer, 0, sizeof (ref_buffer)); for (int n = 0; n < transaction; n++) { sprintf (ref_buffer + 3 * n, "%02d", n); } res = mifare_desfire_read_data (tag, 5, 0, 0, buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (64, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory (buffer, 64, ref_buffer, 64, cut_message ("Wrong data")); int32_t value; res = mifare_desfire_get_value (tag, 4, &value); cut_assert_success ("mifare_desfire_get_value()"); cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); // Reading records from an empty file would abort the transaction if (0 != transaction) { // Get the latest record res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); cut_assert_success ("mifare_desfire_read_records()"); sprintf (ref_buffer, "r.%02d", transaction); cut_assert_not_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); } // Commit ! res = mifare_desfire_commit_transaction (tag); cut_assert_success ("mifare_desfire_commit_transaction()"); res = mifare_desfire_read_data (tag, 5, 3*transaction, 3, buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_memory (data_buffer, 3, buffer, res, cut_message ("Wrong data")); expected_value += 3; res = mifare_desfire_get_value (tag, 4, &value); cut_assert_success ("mifare_desfire_get_value()"); cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); cut_assert_success ("mifare_desfire_read_records()"); sprintf (ref_buffer, "r.%02d", transaction); cut_assert_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); } // Ensure limited credit is disabled res = mifare_desfire_limited_credit (tag, 4, 20); cut_assert_equal_int (-1, res, cut_message ("mifare_desfire_limited_credit() should fail")); // Get all files uint8_t *files; size_t file_count; res = mifare_desfire_get_file_ids (tag, &files, &file_count); cut_assert_success ("mifare_desfire_get_file_ids()"); cut_assert_equal_int (4, file_count, cut_message ("Wrong number of files")); for (size_t i=0; i * * $Id$ */ #include #include #include #include #include #include "freefare_internal.h" #include "mifare_desfire_ev1_fixture.h" #include "common/mifare_desfire_auto_authenticate.h" #define cut_assert_success(last_command) \ do { \ cut_assert_equal_int (OPERATION_OK, mifare_desfire_last_picc_error (tag), cut_message ("PICC replied %s", mifare_desfire_error_lookup (mifare_desfire_last_picc_error (tag)))); \ cut_assert_not_equal_int (-1, res, cut_message ("Wrong return value")); \ } while (0) void test_mifare_desfire_ev1_aes (void) { int res; /* Select the master application */ res = mifare_desfire_select_application (tag, NULL); cut_assert_success ("mifare_desfire_select_application()"); /* Get version information */ struct mifare_desfire_version_info version_info; res = mifare_desfire_get_version (tag, &version_info); cut_assert_success ("mifare_desfire_get_version()"); mifare_desfire_auto_authenticate (tag, 0); /* * This unit test change key settings to more restrictive ones, so reset * them to factory defaults in case the previous run failed unexpectedly. */ res = mifare_desfire_change_key_settings (tag, 0xF); cut_assert_success ("mifare_desfire_change_key_settings()"); /* Change master key to AES */ MifareDESFireKey key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version); mifare_desfire_change_key (tag, 0, key, NULL); cut_assert_success ("mifare_desfire_change_key()"); res = mifare_desfire_authenticate_aes (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate_aes()"); mifare_desfire_key_free (key); /* Wipeout the card */ res = mifare_desfire_format_picc (tag); cut_assert_success ("mifare_desfire_format_picc()"); /* Create 3 applications */ res = mifare_desfire_select_application (tag, NULL); cut_assert_success ("mifare_desfire_select_application()"); key = mifare_desfire_aes_key_new (key_data_aes); res = mifare_desfire_authenticate_aes (tag, 0, key); cut_assert_success ("mifare_desfire_authenticate_aes()"); mifare_desfire_key_free (key); uint8_t key_version; res = mifare_desfire_get_key_version (tag, 0, &key_version); cut_assert_success ("mifare_desfire_get_key_version()"); cut_assert_equal_int (key_data_aes_version, key_version, cut_message ("Wrong key_version value.")); uint32_t size; res = mifare_desfire_free_mem (tag, &size); cut_assert_success ("mifare_desfire_free_mem()"); MifareDESFireAID aid_a = mifare_desfire_aid_new (0x00AAAAAA); cut_assert_not_null (aid_a, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application (tag, aid_a, 0xFF, 0); cut_assert_success ("mifare_desfire_create_application()"); MifareDESFireAID aid_b = mifare_desfire_aid_new (0x00BBBBBB); cut_assert_not_null (aid_b, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application_aes (tag, aid_b, 0xEF, 6); cut_assert_success ("mifare_desfire_create_application()"); MifareDESFireAID aid_c = mifare_desfire_aid_new (0x00CCCCCC); cut_assert_not_null (aid_c, cut_message ("Cannot allocate AID")); res = mifare_desfire_create_application_aes (tag, aid_c, 0xC2, 14); cut_assert_success ("mifare_desfire_create_application()"); // Ensure we can find the created applications MifareDESFireAID *aids = NULL; size_t aid_count; res = mifare_desfire_get_application_ids (tag, &aids, &aid_count); cut_assert_success ("mifare_desfire_get_application_ids()"); cut_assert_equal_int (3, aid_count, cut_message ("Wrong application count")); mifare_desfire_free_application_ids (aids); // Create files in the application A res = mifare_desfire_select_application (tag, aid_a); cut_assert_success ("mifare_desfire_select_application()"); uint8_t std_data_file_id = 15; res = mifare_desfire_create_std_data_file (tag, std_data_file_id, MDCM_PLAIN, 0xEEEE, 100); cut_assert_success ("mifare_desfire_create_std_data_file()"); res = mifare_desfire_create_backup_data_file (tag, 5, MDCM_PLAIN, 0xEEEE, 64); cut_assert_success ("mifare_desfire_create_backup_data_file()"); res = mifare_desfire_create_value_file (tag, 4, MDCM_PLAIN, 0xEEEE, 0, 1000, 0, 0); cut_assert_success ("mifare_desfire_create_value_file()"); res = mifare_desfire_create_cyclic_record_file (tag, 0, MDCM_PLAIN, 0xEEEE, 4, 10); cut_assert_success ("mifare_desfire_create_cyclic_record_file()"); // Write some data in the standard data file res = mifare_desfire_write_data (tag, std_data_file_id, 0, 30, (uint8_t *)"Some data to write to the card"); cut_assert_success ("mifare_desfire_write_data()"); cut_assert_equal_int (30, res, cut_message ("Wrong number of bytes writen")); res = mifare_desfire_write_data (tag, std_data_file_id, 34, 22, (uint8_t *)"Another block of data."); cut_assert_success ("mifare_desfire_write_data()"); cut_assert_equal_int (22, res, cut_message ("Wrong number of bytes writen")); // Make the file read-only res = mifare_desfire_change_file_settings (tag, std_data_file_id, MDCM_PLAIN, 0xEFFF); cut_assert_success ("mifare_desfire_change_file_settings()"); // Read a part of the file uint8_t buffer[120]; res = mifare_desfire_read_data (tag, std_data_file_id, 10, 50, &buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (50, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory ("to write to the card\0\0\0\0Another block of data.\0\0\0\0", 50, buffer, 50, cut_message ("Wrong data")); // Read all the file at once res = mifare_desfire_read_data (tag, std_data_file_id, 0, 0, &buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (100, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory ("Some data to write to the" " card\0\0\0\0Another block of" " data.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 100, buffer, 100, cut_message ("Wrong data")); // Try to overwrute the file res = mifare_desfire_write_data (tag, std_data_file_id, 20, 5, (char *)"Test!"); cut_assert_equal_int (-1, res, cut_message ("Wrong return value")); cut_assert_equal_int (PERMISSION_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Wrong PICC error")); int32_t expected_value = 0; for (int transaction = 0; transaction < 15; transaction++) { char data_buffer[3]; sprintf (data_buffer, "%02d", transaction); // Write to the backup file res = mifare_desfire_write_data (tag, 5, 3*transaction, 3, data_buffer); cut_assert_success ("mifare_desfire_write_data()"); // Manipulate the value file res = mifare_desfire_credit (tag, 4, 100); cut_assert_success ("mifare_desfire_credit()"); res = mifare_desfire_debit (tag, 4, 97); cut_assert_success ("mifare_desfire_debit()"); // Write to the cyclic record file res = mifare_desfire_write_record (tag, 0, 2, 2, data_buffer); cut_assert_success ("mifare_desfire_write_record()"); // Overwrite the cyclic record file res = mifare_desfire_write_record (tag, 0, 0, 2, (char *)"r."); cut_assert_success("mifare_desfire_write_record()"); // Ensure that no content was changed yet char ref_buffer[64]; memset (ref_buffer, 0, sizeof (ref_buffer)); for (int n = 0; n < transaction; n++) { sprintf (ref_buffer + 3 * n, "%02d", n); } res = mifare_desfire_read_data (tag, 5, 0, 0, buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_int (64, res, cut_message ("Wrong number of bytes read")); cut_assert_equal_memory (buffer, 64, ref_buffer, 64, cut_message ("Wrong data")); int32_t value; res = mifare_desfire_get_value (tag, 4, &value); cut_assert_success ("mifare_desfire_get_value()"); cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); // Reading records from an empty file would abort the transaction if (0 != transaction) { // Get the latest record res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); cut_assert_success ("mifare_desfire_read_records()"); sprintf (ref_buffer, "r.%02d", transaction); cut_assert_not_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); } // Commit ! res = mifare_desfire_commit_transaction (tag); cut_assert_success ("mifare_desfire_commit_transaction()"); res = mifare_desfire_read_data (tag, 5, 3*transaction, 3, buffer); cut_assert_success ("mifare_desfire_read_data()"); cut_assert_equal_memory (data_buffer, 3, buffer, res, cut_message ("Wrong data")); expected_value += 3; res = mifare_desfire_get_value (tag, 4, &value); cut_assert_success ("mifare_desfire_get_value()"); cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); cut_assert_success ("mifare_desfire_read_records()"); sprintf (ref_buffer, "r.%02d", transaction); cut_assert_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); } // Ensure limited credit is disabled res = mifare_desfire_limited_credit (tag, 4, 20); cut_assert_equal_int (-1, res, cut_message ("mifare_desfire_limited_credit() should fail")); // Get all files uint8_t *files; size_t file_count; res = mifare_desfire_get_file_ids (tag, &files, &file_count); cut_assert_success ("mifare_desfire_get_file_ids()"); cut_assert_equal_int (4, file_count, cut_message ("Wrong number of files")); for (size_t i=0; i * * $Id$ */ #include #include #include "freefare_internal.h" #include "mifare_desfire_ev1_fixture.h" #include "common/mifare_desfire_auto_authenticate.h" #define cut_assert_success(last_command) \ do { \ cut_assert_equal_int (OPERATION_OK, mifare_desfire_last_picc_error (tag), cut_message ("PICC replied %s", mifare_desfire_error_lookup (mifare_desfire_last_picc_error (tag)))); \ cut_assert_not_equal_int (-1, res, cut_message ("Wrong return value")); \ } while (0) void test_mifare_desfire_ev1_iso (void) { int res; mifare_desfire_auto_authenticate (tag, 0); res = mifare_desfire_format_picc (tag); cut_assert_equal_int (res, 0, cut_message ("mifare_desfire_format_picc()")); MifareDESFireDF *dfs; size_t count; res = mifare_desfire_get_df_names (tag, &dfs, &count); cut_assert_equal_int (res, 0, cut_message ("mifare_desfire_get_df_names()")); cut_assert_equal_int (count, 0, cut_message ("Wrong DF count")); cut_assert_null (dfs, cut_message ("DF should be NULL")); MifareDESFireAID aid = mifare_desfire_aid_new (0x111110); res = mifare_desfire_create_application_iso (tag, aid, 0xFF, 1, 0, 0x111F, NULL, 0); cut_assert_success ("mifare_desfire_create_application_iso"); free (aid); uint8_t app2[] = "App2"; aid = mifare_desfire_aid_new (0x222220); res = mifare_desfire_create_application_iso (tag, aid, 0xFF, 1, 0, 0x222F, app2, sizeof (app2)); cut_assert_success ("mifare_desfire_create_application_iso"); free (aid); uint8_t app3[] = "App3"; aid = mifare_desfire_aid_new (0x333330); res = mifare_desfire_create_application_iso (tag, aid, 0xFF, 1, 0, 0x333F, app3, sizeof (app3)); cut_assert_success ("mifare_desfire_create_application_iso"); free (aid); aid = mifare_desfire_aid_new (0x444440); res = mifare_desfire_create_application_iso (tag, aid, 0xFF, 1, 0, 0x111F, NULL, 0); cut_assert_equal_int (-1, res, cut_message ("Should fail")); cut_assert_equal_int (DUPLICATE_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Should be a duplicate error")); res = mifare_desfire_create_application_iso (tag, aid, 0xFF, 1, 0, 0x444F, app2, sizeof (app2)); cut_assert_equal_int (-1, res, cut_message ("Should fail")); cut_assert_equal_int (DUPLICATE_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Should be a duplicate error")); free (aid); res = mifare_desfire_get_df_names (tag, &dfs, &count); cut_assert_equal_int (0, res, cut_message ("mifare_desfire_get_df_names()")); cut_assert_equal_int (3, count, cut_message ("Wrong DF count")); cut_assert_not_null (dfs, cut_message ("DF should not be NULL")); cut_assert_equal_int (0x111110, dfs[0].aid, cut_message ("Wrong value")); cut_assert_equal_int (0x111F, dfs[0].fid, cut_message ("Wrong value")); cut_assert_equal_int (0, dfs[0].df_name_len, cut_message ("Wrong value")); cut_assert_equal_int (0x222220, dfs[1].aid, cut_message ("Wrong value")); cut_assert_equal_int (0x222F, dfs[1].fid, cut_message ("Wrong value")); cut_assert_equal_int (sizeof (app2), dfs[1].df_name_len, cut_message ("Wrong value")); cut_assert_equal_memory (app2, sizeof (app2), dfs[1].df_name, dfs[1].df_name_len, cut_message ("Wrong value")); cut_assert_equal_int (0x333330, dfs[2].aid, cut_message ("Wrong value")); cut_assert_equal_int (0x333F, dfs[2].fid, cut_message ("Wrong value")); cut_assert_equal_int (sizeof (app3), dfs[2].df_name_len, cut_message ("Wrong value")); cut_assert_equal_memory (app3, sizeof (app3), dfs[2].df_name, dfs[2].df_name_len, cut_message ("Wrong value")); free (dfs); aid = mifare_desfire_aid_new (0x555550); res = mifare_desfire_create_application_iso (tag, aid, 0xff, 1, 1, 0x555F, NULL, 0); cut_assert_success ("mifare_desfire_create_application_iso"); res = mifare_desfire_select_application (tag, aid); cut_assert_success ("mifare_desfire_select_application"); res = mifare_desfire_create_std_data_file_iso (tag, 1, MDCM_PLAIN, 0xEEEE, 32, 0x1234); cut_assert_success ("mifare_desfire_create_std_data_file_iso"); res = mifare_desfire_create_backup_data_file_iso (tag, 2, MDCM_PLAIN, 0xEEEE, 32, 0x2345); cut_assert_success ("mifare_desfire_create_std_data_file_iso"); res = mifare_desfire_create_linear_record_file_iso (tag, 3, MDCM_PLAIN, 0xEEEE, 32, 10, 0x3456); cut_assert_success ("mifare_desfire_create_linear_record_file_iso"); res = mifare_desfire_create_cyclic_record_file_iso (tag, 4, MDCM_PLAIN, 0xEEEE, 32, 10, 0x4567); cut_assert_success ("mifare_desfire_create_cyclic_record_file_iso"); uint16_t *ids; res = mifare_desfire_get_iso_file_ids (tag, &ids, &count); cut_assert_success ("mifare_desfire_get_iso_file_ids"); cut_assert_equal_int (4, count, cut_message ("Invalid file count")); cut_assert_equal_int (0x1234, ids[0], cut_message ("Wrong file ID")); cut_assert_equal_int (0x2345, ids[1], cut_message ("Wrong file ID")); cut_assert_equal_int (0x3456, ids[2], cut_message ("Wrong file ID")); cut_assert_equal_int (0x4567, ids[3], cut_message ("Wrong file ID")); free (ids); free (aid); } libfreefare-0.4.0/test/test_mifare_desfire_key.c000066400000000000000000000101521225640673100217640ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include void test_mifare_desfire_key (void) { MifareDESFireKey key; int version; uint8_t key1_des_data[8] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; key = mifare_desfire_des_key_new (key1_des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); key = mifare_desfire_des_key_new_with_version (key1_des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x55, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_set_version (key, 0xaa); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0xaa, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); uint8_t key2_des_data[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; key = mifare_desfire_des_key_new (key2_des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); key = mifare_desfire_des_key_new_with_version (key2_des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); uint8_t key1_3des_data[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0XEE, 0xFF }; key = mifare_desfire_3des_key_new (key1_3des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); key = mifare_desfire_3des_key_new_with_version (key1_3des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x55, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_set_version (key, 0xaa); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0xaa, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); uint8_t key2_3des_data[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0X01, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; key = mifare_desfire_3des_key_new (key2_3des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); key = mifare_desfire_3des_key_new_with_version (key2_3des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x02, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); uint8_t key3_3des_data[16] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0X00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77 }; key = mifare_desfire_3des_key_new (key3_3des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); key = mifare_desfire_3des_key_new_with_version (key3_3des_data); version = mifare_desfire_key_get_version (key); cut_assert_equal_int (0x10, version, cut_message ("Wrong MifareDESFireKey version")); mifare_desfire_key_free (key); } libfreefare-0.4.0/test/test_mifare_ultralight.c000066400000000000000000000160531225640673100216600ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include #include #include #include "freefare_internal.h" #include "mifare_ultralight_fixture.h" void test_mifare_ultralight_write (void) { int res; MifareUltralightPage initial; MifareUltralightPage page; MifareUltralightPage payload1 = { 0x12, 0x34, 0x56, 0x78 }; MifareUltralightPage payload2 = { 0xaa, 0x55, 0x00, 0xff }; MifareUltralightPageNumber n = 7; /* Read and save current value (should be { 0x00 0x00 0x00 0x00 }) */ res = mifare_ultralight_read (tag, n, &initial); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed")); /* Write payload1 */ res = mifare_ultralight_write (tag, n, payload1); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_write() failed")); /* Check it */ res = mifare_ultralight_read (tag, n, &page); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed")); cut_assert_equal_memory (payload1, sizeof (payload1), page, sizeof (page), cut_message ("Wrong data")); /* Write payload2 */ res = mifare_ultralight_write (tag, n, payload2); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_write() failed")); /* Check it */ res = mifare_ultralight_read (tag, n, &page); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed")); cut_assert_equal_memory (payload2, sizeof (payload2), page, sizeof (page), cut_message ("Wrong data")); /* Write initial data */ res = mifare_ultralight_write (tag, n, initial); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_write() failed")); /* While here check it (no reason to fail since the rest of the test passed) */ res = mifare_ultralight_read (tag, n, &page); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed")); cut_assert_equal_memory (initial, sizeof (initial), page, sizeof (page), cut_message ("Wrong data")); } void test_mifare_ultralight_invalid_page (void) { int res; MifareUltralightPage page = { 0x00, 0x00, 0x00, 0x00 }; int invalid_page; if (IS_MIFARE_ULTRALIGHT_C (tag)) { invalid_page = MIFARE_ULTRALIGHT_C_PAGE_COUNT; } else { invalid_page = MIFARE_ULTRALIGHT_PAGE_COUNT; } res = mifare_ultralight_read (tag, invalid_page, &page); cut_assert_equal_int (-1, res, cut_message ("mifare_ultralight_read() succeeded")); cut_assert_equal_int (EINVAL, errno, cut_message ("Wrong errno value")); res = mifare_ultralight_write (tag, invalid_page, page); cut_assert_equal_int (-1, res, cut_message ("mifare_ultralight_write() succeeded")); cut_assert_equal_int (EINVAL, errno, cut_message ("Wrong errno value")); } void test_mifare_ultralight_cache (void) { int res; MifareUltralightPage page; res = mifare_ultralight_read (tag, 0, &page); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed")); /* Check cached pages consistency */ for (int i = 0; i <= 3; i++) { cut_assert_equal_int (1, MIFARE_ULTRALIGHT(tag)->cached_pages[i], cut_message ("Wrong page cache value for tag->cached_pages[%d]", i)); } for (int i = 4; i < MIFARE_ULTRALIGHT_PAGE_COUNT; i++) { cut_assert_equal_int (0, MIFARE_ULTRALIGHT(tag)->cached_pages[i], cut_message ("Wrong page cache value for tag->cached_pages[%d]", i)); } } void test_mifare_ultralight_cache_hit (void) { int res; MifareUltralightPage page1; MifareUltralightPage page2; res = mifare_ultralight_read (tag, 0, &page1); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed")); res = mifare_ultralight_read (tag, 0, &page2); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed")); cut_assert_equal_memory (page1, sizeof (page1), page2, sizeof (page2), cut_message ("Wrong cached data")); } void test_mifare_ultralight_cache_wrap (void) { int res; MifareUltralightPage page; int last_page; if (IS_MIFARE_ULTRALIGHT_C (tag)) { // Last 4 blocks are for 3DES key and cannot be read, read will wrap from 0x2b last_page = MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ -1; // Actually engineering samples require auth to read above page 0x28 so we skip the test entirely cut_omit("mifare_ultralight_read() on last page skipped on UltralightC"); } else { last_page = MIFARE_ULTRALIGHT_PAGE_COUNT -1; } res = mifare_ultralight_read (tag, last_page, &page); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed")); /* Check cached pages consistency */ for (int i = 0; i <= 2; i++) { cut_assert_equal_int (1, MIFARE_ULTRALIGHT(tag)->cached_pages[i], cut_message ("Wrong page cache value for tag->cached_pages[%d]", i)); } for (int i = 3; i < last_page; i++) { cut_assert_equal_int (0, MIFARE_ULTRALIGHT(tag)->cached_pages[i], cut_message ("Wrong page cache value for tag->cached_pages[%d]", i)); } cut_assert_equal_int (1, MIFARE_ULTRALIGHT(tag)->cached_pages[last_page], cut_message ("Wrong page cache value for tag->cached_pages[%d]", last_page)); } void test_mifare_ultralight_get_uid (void) { char *uid; uid = freefare_get_tag_uid (tag); cut_assert_not_null (uid, cut_message ("mifare_ultralight_get_uid() failed")); cut_assert_equal_int (14, strlen (uid), cut_message ("Wrong UID length")); free (uid); } void test_mifare_ultralight_tag_friendly_name (void) { const char *name = freefare_get_tag_friendly_name (tag); cut_assert_not_null (name, cut_message ("freefare_get_tag_friendly_name() failed")); } void test_mifare_ultralightc_authenticate (void) { int res; MifareDESFireKey key; if (tag->tag_info->type == ULTRALIGHT_C) { uint8_t key1_3des_data[16] = { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }; key = mifare_desfire_3des_key_new (key1_3des_data); res = mifare_ultralightc_authenticate (tag, key); cut_assert_equal_int (0, res, cut_message ("mifare_ultralightc_authenticate() failed")); mifare_desfire_key_free (key); MifareUltralightPage page; int last_page = MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ -1; res = mifare_ultralight_read (tag, last_page, &page); cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed")); } else { cut_omit("mifare_ultralightc_authenticate() skipped on Ultralight"); } } libfreefare-0.4.0/test/test_tlv.c000066400000000000000000000127021225640673100167600ustar00rootroot00000000000000/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * $Id$ */ #include #include const uint8_t shortdata[8] = "elephant"; const uint8_t eshortdata[11] = "\x03" "\x08" "elephant" "\xfe"; /* * Many thanks to Charles Baudelaire for helping me * test things and helping you realize your f**king * OS / compiler does not support UTF-8 ;-) */ const uint8_t longdata[660] = "Dans une terre grasse et pleine d'escargots\n" \ "Je veux creuser moi-même une fosse profonde,\n" \ "Où je puisse à loisir étaler mes vieux os\n" \ "Et dormir dans l'oubli comme un requin dans l'onde.\n" \ "Je hais les testaments et je hais les tombeaux;\n" \ "Plutôt que d'implorer une larme du monde,\n" \ "Vivant, j'aimerais mieux inviter les corbeaux\n" \ "À saigner tous les bouts de ma carcasse immonde.\n" \ "Ô vers! noirs compagnons sans oreille et sans yeux,\n" \ "Voyez venir à vous un mort libre et joyeux;\n" \ "Philosophes viveurs, fils de la pourriture,\n" \ "À travers ma ruine allez donc sans remords,\n" \ "Et dites-moi s'il est encor quelque torture\n" \ "Pour ce vieux corps sans âme et mort parmi les morts!\n"; const uint8_t elongdata[665] = "\x07" "\xff\x02\x94" \ "Dans une terre grasse et pleine d'escargots\n" \ "Je veux creuser moi-même une fosse profonde,\n" \ "Où je puisse à loisir étaler mes vieux os\n" \ "Et dormir dans l'oubli comme un requin dans l'onde.\n" \ "Je hais les testaments et je hais les tombeaux;\n" \ "Plutôt que d'implorer une larme du monde,\n" \ "Vivant, j'aimerais mieux inviter les corbeaux\n" \ "À saigner tous les bouts de ma carcasse immonde.\n" \ "Ô vers! noirs compagnons sans oreille et sans yeux,\n" \ "Voyez venir à vous un mort libre et joyeux;\n" \ "Philosophes viveurs, fils de la pourriture,\n" \ "À travers ma ruine allez donc sans remords,\n" \ "Et dites-moi s'il est encor quelque torture\n" \ "Pour ce vieux corps sans âme et mort parmi les morts!\n" "\xfe"; void test_tlv_encode_short (void) { uint8_t *res; size_t osize; res = tlv_encode (3, shortdata, sizeof (shortdata), &osize); cut_assert_equal_int (sizeof (eshortdata), osize, cut_message ("Wrong encoded message length.")); cut_assert_equal_int (3, res[0], cut_message ("Wrong type")); cut_assert_equal_int (sizeof (shortdata), res[1], cut_message ("Wrong value length")); cut_assert_equal_memory (eshortdata, sizeof (eshortdata), res, osize, cut_message ("Wrong encoded value")); free (res); } void test_tlv_encode_long (void) { uint8_t *res; size_t osize; res = tlv_encode (7, longdata, sizeof (longdata), &osize); cut_assert_equal_int (sizeof (elongdata), osize, cut_message ("Wrong encoded message length.")); cut_assert_equal_int (7, res[0], cut_message ("Wrong type")); cut_assert_equal_int (0xff, res[1], cut_message ("Wrong value length")); cut_assert_equal_int (0x02, res[2], cut_message ("Wrong value length")); cut_assert_equal_int (0x94, res[3], cut_message ("Wrong value length")); cut_assert_equal_memory (elongdata, sizeof (elongdata), res, osize, cut_message ("Wrong encoded value")); free (res); } void test_tlv_decode_short (void) { uint8_t *res; uint16_t size; uint8_t type; res = tlv_decode (eshortdata, &type, &size); cut_assert_equal_int (3, type, cut_message ("Wrong type")); cut_assert_equal_int (sizeof (shortdata), size, cut_message ("Wrong value length")); cut_assert_equal_memory (shortdata, sizeof (shortdata), res, size, cut_message ("Wrong decoded value")); free (res); } void test_tlv_decode_long (void) { uint8_t *res; uint16_t size; uint8_t type; res = tlv_decode (elongdata, &type, &size); cut_assert_equal_int (7, type, cut_message ("Wrong type")); cut_assert_equal_int (sizeof (longdata), size, cut_message ("Wrong value length")); cut_assert_equal_memory (longdata, sizeof (longdata), res, size, cut_message ("Wrong decoded value")); free (res); } void test_tlv_rfu (void) { uint8_t *data = malloc (0xffff); cut_assert_not_null (data, cut_message ("Out of memory")); uint8_t *res = tlv_encode (7, data, 0xffff, NULL); cut_assert_null (res, cut_message ("Size reserved for future use")); free (data); } void test_tlv_append (void) { const uint8_t a[] = { 0xde, 0xad, 0xbe, 0xef }; const uint8_t b[] = { 0x42 }; uint8_t ndef_ab_ref[] = { 0x03, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x03, 0x01, 0x42, 0xfe }; uint8_t *ndef_a = tlv_encode (3, a, 4, NULL); uint8_t *ndef_b = tlv_encode (3, b, 1, NULL); ndef_a = tlv_append (ndef_a, ndef_b); cut_assert_equal_memory (ndef_ab_ref, sizeof (ndef_ab_ref), ndef_a, sizeof (ndef_ab_ref), cut_message ("Wrong appended data")); free (ndef_a); free (ndef_b); }