nfc-mfclassic.c

Go to the documentation of this file.
00001 /*-
00002  * Public platform independent Near Field Communication (NFC) library
00003  * 
00004  * Copyright (C) 2009, Roel Verdult
00005  * 
00006  * This program is free software: you can redistribute it and/or modify it
00007  * under the terms of the GNU Lesser General Public License as published by the
00008  * Free Software Foundation, either version 3 of the License, or (at your
00009  * option) any later version.
00010  * 
00011  * This program is distributed in the hope that it will be useful, but WITHOUT
00012  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00013  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00014  * more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 
00025 #ifdef HAVE_CONFIG_H
00026 #  include "config.h"
00027 #endif // HAVE_CONFIG_H
00028 
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <stdint.h>
00032 #include <stddef.h>
00033 #include <stdbool.h>
00034 
00035 #include <string.h>
00036 #include <ctype.h>
00037 
00038 #include <nfc/nfc.h>
00039 
00040 #include "mifare.h"
00041 #include "nfc-utils.h"
00042 
00043 static nfc_device_t *pnd;
00044 static nfc_target_info_t nti;
00045 static mifare_param mp;
00046 static mifare_classic_tag mtKeys;
00047 static mifare_classic_tag mtDump;
00048 static bool bUseKeyA;
00049 static bool bUseKeyFile;
00050 static uint8_t uiBlocks;
00051 static byte_t keys[] = {
00052   0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
00053   0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7,
00054   0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
00055   0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
00056   0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd,
00057   0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a,
00058   0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
00059   0x00, 0x00, 0x00, 0x00, 0x00, 0x00
00060 };
00061 
00062 static size_t num_keys = sizeof (keys) / 6;
00063 
00064 static void
00065 print_success_or_failure (bool bFailure, uint32_t * uiBlockCounter)
00066 {
00067   printf ("%c", (bFailure) ? 'x' : '.');
00068   if (uiBlockCounter && !bFailure)
00069     *uiBlockCounter += (*uiBlockCounter < 128) ? 4 : 16;
00070 }
00071 
00072 static  bool
00073 is_first_block (uint32_t uiBlock)
00074 {
00075   // Test if we are in the small or big sectors
00076   if (uiBlock < 128)
00077     return ((uiBlock) % 4 == 0);
00078   else
00079     return ((uiBlock) % 16 == 0);
00080 }
00081 
00082 static  bool
00083 is_trailer_block (uint32_t uiBlock)
00084 {
00085   // Test if we are in the small or big sectors
00086   if (uiBlock < 128)
00087     return ((uiBlock + 1) % 4 == 0);
00088   else
00089     return ((uiBlock + 1) % 16 == 0);
00090 }
00091 
00092 static  uint32_t
00093 get_trailer_block (uint32_t uiFirstBlock)
00094 {
00095   // Test if we are in the small or big sectors
00096   uint32_t trailer_block = 0;
00097   if (uiFirstBlock < 128) {
00098     trailer_block = uiFirstBlock + (3 - (uiFirstBlock % 4));
00099   } else {
00100     trailer_block = uiFirstBlock + (15 - (uiFirstBlock % 16));
00101   }
00102   return trailer_block;
00103 }
00104 
00105 static  bool
00106 authenticate (uint32_t uiBlock)
00107 {
00108   mifare_cmd mc;
00109   uint32_t uiTrailerBlock;
00110   size_t  key_index;
00111 
00112   // Key file authentication.
00113   if (bUseKeyFile) {
00114     // Set the authentication information (uid)
00115     memcpy (mp.mpa.abtUid, nti.nai.abtUid, 4);
00116 
00117     // Locate the trailer (with the keys) used for this sector
00118     uiTrailerBlock = get_trailer_block (uiBlock);
00119 
00120     // Determin if we should use the a or the b key
00121     if (bUseKeyA) {
00122       mc = MC_AUTH_A;
00123       memcpy (mp.mpa.abtKey, mtKeys.amb[uiTrailerBlock].mbt.abtKeyA, 6);
00124     } else {
00125       mc = MC_AUTH_B;
00126       memcpy (mp.mpa.abtKey, mtKeys.amb[uiTrailerBlock].mbt.abtKeyB, 6);
00127     }
00128 
00129     // Try to authenticate for the current sector
00130     if (nfc_initiator_mifare_cmd (pnd, mc, uiBlock, &mp))
00131       return true;
00132   }
00133   // Auto authentication.
00134   else {
00135     // Determin if we should use the a or the b key
00136     mc = (bUseKeyA) ? MC_AUTH_A : MC_AUTH_B;
00137 
00138     // Set the authentication information (uid)
00139     memcpy (mp.mpa.abtUid, nti.nai.abtUid, 4);
00140 
00141     for (key_index = 0; key_index < num_keys; key_index++) {
00142       memcpy (mp.mpa.abtKey, keys + (key_index * 6), 6);
00143       if (nfc_initiator_mifare_cmd (pnd, mc, uiBlock, &mp)) {
00144         if (bUseKeyA)
00145           memcpy (mtKeys.amb[uiBlock].mbt.abtKeyA, &mp.mpa.abtKey, 6);
00146         else
00147           memcpy (mtKeys.amb[uiBlock].mbt.abtKeyB, &mp.mpa.abtKey, 6);
00148 
00149         return true;
00150       }
00151 
00152       nfc_initiator_select_passive_target (pnd, NM_ISO14443A_106, mp.mpa.abtUid, 4, NULL);
00153     }
00154   }
00155 
00156   return false;
00157 }
00158 
00159 static  bool
00160 read_card (void)
00161 {
00162   int32_t iBlock;
00163   bool    bFailure = false;
00164   uint32_t uiReadBlocks = 0;
00165 
00166   printf ("Reading out %d blocks |", uiBlocks + 1);
00167 
00168   // Read the card from end to begin
00169   for (iBlock = uiBlocks; iBlock >= 0; iBlock--) {
00170     // Authenticate everytime we reach a trailer block
00171     if (is_trailer_block (iBlock)) {
00172       // Skip this the first time, bFailure it means nothing (yet)
00173       if (iBlock != uiBlocks)
00174         print_success_or_failure (bFailure, &uiReadBlocks);
00175 
00176       // Show if the readout went well
00177       if (bFailure) {
00178         // When a failure occured we need to redo the anti-collision
00179         if (!nfc_initiator_select_passive_target (pnd, NM_ISO14443A_106, NULL, 0, &nti)) {
00180           printf ("!\nError: tag was removed\n");
00181           return false;
00182         }
00183         bFailure = false;
00184       }
00185 
00186       fflush (stdout);
00187 
00188       // Try to authenticate for the current sector
00189       if (!authenticate (iBlock)) {
00190         printf ("!\nError: authentication failed for block 0x%02x\n", iBlock);
00191         return false;
00192       }
00193       // Try to read out the trailer
00194       if (nfc_initiator_mifare_cmd (pnd, MC_READ, iBlock, &mp)) {
00195         // Copy the keys over from our key dump and store the retrieved access bits
00196         memcpy (mtDump.amb[iBlock].mbt.abtKeyA, mtKeys.amb[iBlock].mbt.abtKeyA, 6);
00197         memcpy (mtDump.amb[iBlock].mbt.abtAccessBits, mp.mpd.abtData + 6, 4);
00198         memcpy (mtDump.amb[iBlock].mbt.abtKeyB, mtKeys.amb[iBlock].mbt.abtKeyB, 6);
00199       } else {
00200         printf ("!\nError: unable to read trailer block 0x%02x\n", iBlock);
00201       }
00202     } else {
00203       // Make sure a earlier readout did not fail
00204       if (!bFailure) {
00205         // Try to read out the data block
00206         if (nfc_initiator_mifare_cmd (pnd, MC_READ, iBlock, &mp)) {
00207           memcpy (mtDump.amb[iBlock].mbd.abtData, mp.mpd.abtData, 16);
00208         } else {
00209           bFailure = true;
00210           printf ("!\nError: unable to read block 0x%02x\n", iBlock);
00211           return false;
00212         }
00213       }
00214     }
00215   }
00216   print_success_or_failure (bFailure, &uiReadBlocks);
00217   printf ("|\n");
00218   printf ("Done, %d of %d blocks read.\n", uiReadBlocks, uiBlocks + 1);
00219   fflush (stdout);
00220 
00221   return true;
00222 }
00223 
00224 static  bool
00225 write_card (void)
00226 {
00227   uint32_t uiBlock;
00228   bool    bFailure = false;
00229   uint32_t uiWriteBlocks = 0;
00230 
00231   printf ("Writing %d blocks |", uiBlocks + 1);
00232 
00233   // Write the card from begin to end;
00234   for (uiBlock = 0; uiBlock <= uiBlocks; uiBlock++) {
00235     // Authenticate everytime we reach the first sector of a new block
00236     if (is_first_block (uiBlock)) {
00237       // Skip this the first time, bFailure it means nothing (yet)
00238       if (uiBlock != 0)
00239         print_success_or_failure (bFailure, &uiWriteBlocks);
00240 
00241       // Show if the readout went well
00242       if (bFailure) {
00243         // When a failure occured we need to redo the anti-collision
00244         if (!nfc_initiator_select_passive_target (pnd, NM_ISO14443A_106, NULL, 0, &nti)) {
00245           printf ("!\nError: tag was removed\n");
00246           return false;
00247         }
00248         bFailure = false;
00249       }
00250 
00251       fflush (stdout);
00252 
00253       // Try to authenticate for the current sector
00254       if (!authenticate (uiBlock)) {
00255         printf ("!\nError: authentication failed for block %02x\n", uiBlock);
00256         return false;
00257       }
00258     }
00259 
00260     if (is_trailer_block (uiBlock)) {
00261       // Copy the keys over from our key dump and store the retrieved access bits
00262       memcpy (mp.mpd.abtData, mtDump.amb[uiBlock].mbt.abtKeyA, 6);
00263       memcpy (mp.mpd.abtData + 6, mtDump.amb[uiBlock].mbt.abtAccessBits, 4);
00264       memcpy (mp.mpd.abtData + 10, mtDump.amb[uiBlock].mbt.abtKeyB, 6);
00265 
00266       // Try to write the trailer
00267       if (nfc_initiator_mifare_cmd (pnd, MC_WRITE, uiBlock, &mp) == false) {
00268         printf ("failed to write trailer block %d \n", uiBlock);
00269         bFailure = true;
00270       }
00271     } else {
00272       // The first block 0x00 is read only, skip this
00273       if (uiBlock == 0)
00274         continue;
00275 
00276       // Make sure a earlier write did not fail
00277       if (!bFailure) {
00278         // Try to write the data block
00279         memcpy (mp.mpd.abtData, mtDump.amb[uiBlock].mbd.abtData, 16);
00280         if (!nfc_initiator_mifare_cmd (pnd, MC_WRITE, uiBlock, &mp))
00281           bFailure = true;
00282       }
00283     }
00284   }
00285   print_success_or_failure (bFailure, &uiWriteBlocks);
00286   printf ("|\n");
00287   printf ("Done, %d of %d blocks written.\n", uiWriteBlocks, uiBlocks + 1);
00288   fflush (stdout);
00289 
00290   return true;
00291 }
00292 
00293 static void
00294 mifare_classic_extract_payload (const char *abDump, char *pbPayload)
00295 {
00296   uint8_t uiSectorIndex;
00297   uint8_t uiBlockIndex;
00298   size_t  szDumpOffset;
00299   size_t  szPayloadIndex = 0;
00300 
00301   for (uiSectorIndex = 1; uiSectorIndex < 16; uiSectorIndex++) {
00302     for (uiBlockIndex = 0; uiBlockIndex < 3; uiBlockIndex++) {
00303       szDumpOffset = uiSectorIndex * 16 * 4 + uiBlockIndex * 16;
00304 //      for(uint8_t uiByteIndex=0; uiByteIndex<16; uiByteIndex++) printf("%02x ", abDump[szPayloadIndex+uiByteIndex]);
00305       memcpy (pbPayload + szPayloadIndex, abDump + szDumpOffset, 16);
00306       szPayloadIndex += 16;
00307     }
00308   }
00309 }
00310 
00311 typedef enum {
00312   ACTION_READ,
00313   ACTION_WRITE,
00314   ACTION_EXTRACT,
00315   ACTION_USAGE
00316 } action_t;
00317 
00318 static void
00319 print_usage (const char *pcProgramName)
00320 {
00321   printf ("Usage: ");
00322   printf ("%s r|w a|b <dump.mfd> [<keys.mfd>]\n", pcProgramName);
00323   printf ("  r|w           - Perform read from (r) or write to (w) card\n");
00324   printf ("  a|b           - Use A or B keys for action\n");
00325   printf ("  <dump.mfd>    - MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)\n");
00326   printf ("  <keys.mfd>    - MiFare Dump (MFD) that contain the keys (optional)\n");
00327   printf ("Or: ");
00328   printf ("%s x <dump.mfd> <payload.bin>\n", pcProgramName);
00329   printf ("  x             - Extract payload (data blocks) from MFD\n");
00330   printf ("  <dump.mfd>    - MiFare Dump (MFD) that contains wanted payload\n");
00331   printf ("  <payload.bin> - Binary file where payload will be extracted\n");
00332 }
00333 
00334 int
00335 main (int argc, const char *argv[])
00336 {
00337   bool    b4K;
00338   action_t atAction = ACTION_USAGE;
00339   byte_t *pbtUID;
00340   FILE   *pfKeys = NULL;
00341   FILE   *pfDump = NULL;
00342   const char *command = argv[1];
00343 
00344   if (argc < 2) {
00345     print_usage (argv[0]);
00346     exit (EXIT_FAILURE);
00347   }
00348 
00349   if (strcmp (command, "r") == 0) {
00350     atAction = ACTION_READ;
00351     bUseKeyA = tolower ((int) ((unsigned char) *(argv[2]))) == 'a';
00352     bUseKeyFile = (argc > 4);
00353   } else if (strcmp (command, "w") == 0) {
00354     atAction = ACTION_WRITE;
00355     bUseKeyA = tolower ((int) ((unsigned char) *(argv[2]))) == 'a';
00356     bUseKeyFile = (argc > 4);
00357   } else if (strcmp (command, "x") == 0) {
00358     atAction = ACTION_EXTRACT;
00359   }
00360 
00361   switch (atAction) {
00362   case ACTION_USAGE:
00363     print_usage (argv[0]);
00364     exit (EXIT_FAILURE);
00365     break;
00366   case ACTION_READ:
00367   case ACTION_WRITE:
00368     if (argc < 4) {
00369       print_usage (argv[0]);
00370       exit (EXIT_FAILURE);
00371     }
00372 
00373     if (bUseKeyFile) {
00374       pfKeys = fopen (argv[4], "rb");
00375       if (pfKeys == NULL) {
00376         printf ("Could not open keys file: %s\n", argv[4]);
00377         exit (EXIT_FAILURE);
00378       }
00379       if (fread (&mtKeys, 1, sizeof (mtKeys), pfKeys) != sizeof (mtKeys)) {
00380         printf ("Could not read keys file: %s\n", argv[4]);
00381         fclose (pfKeys);
00382         exit (EXIT_FAILURE);
00383       }
00384       fclose (pfKeys);
00385     }
00386 
00387     if (atAction == ACTION_READ) {
00388       memset (&mtDump, 0x00, sizeof (mtDump));
00389     } else {
00390       pfDump = fopen (argv[3], "rb");
00391 
00392       if (pfDump == NULL) {
00393         printf ("Could not open dump file: %s\n", argv[3]);
00394         exit (EXIT_FAILURE);
00395       }
00396 
00397       if (fread (&mtDump, 1, sizeof (mtDump), pfDump) != sizeof (mtDump)) {
00398         printf ("Could not read dump file: %s\n", argv[3]);
00399         fclose (pfDump);
00400         exit (EXIT_FAILURE);
00401       }
00402       fclose (pfDump);
00403     }
00404     // printf("Successfully opened required files\n");
00405 
00406     // Try to open the NFC reader
00407     pnd = nfc_connect (NULL);
00408     if (pnd == NULL) {
00409       printf ("Error connecting NFC reader\n");
00410       exit (EXIT_FAILURE);
00411     }
00412 
00413     nfc_initiator_init (pnd);
00414 
00415     // Drop the field for a while
00416     if (!nfc_configure (pnd, NDO_ACTIVATE_FIELD, false)) {
00417       nfc_perror (pnd, "nfc_configure");
00418       exit (EXIT_FAILURE);
00419     }
00420     // Let the reader only try once to find a tag
00421     if (!nfc_configure (pnd, NDO_INFINITE_SELECT, false)) {
00422       nfc_perror (pnd, "nfc_configure");
00423       exit (EXIT_FAILURE);
00424     }
00425     if (!nfc_configure (pnd, NDO_HANDLE_CRC, true)) {
00426       nfc_perror (pnd, "nfc_configure");
00427       exit (EXIT_FAILURE);
00428     }
00429     if (!nfc_configure (pnd, NDO_HANDLE_PARITY, true)) {
00430       nfc_perror (pnd, "nfc_configure");
00431       exit (EXIT_FAILURE);
00432     }
00433     // Enable field so more power consuming cards can power themselves up
00434     if (!nfc_configure (pnd, NDO_ACTIVATE_FIELD, true)) {
00435       nfc_perror (pnd, "nfc_configure");
00436       exit (EXIT_FAILURE);
00437     }
00438     // Disable ISO14443-4 switching in order to read devices that emulate Mifare Classic with ISO14443-4 compliance.
00439     nfc_configure (pnd, NDO_AUTO_ISO14443_4, false);
00440 
00441     printf ("Connected to NFC reader: %s\n", pnd->acName);
00442 
00443     // Try to find a MIFARE Classic tag
00444     if (!nfc_initiator_select_passive_target (pnd, NM_ISO14443A_106, NULL, 0, &nti)) {
00445       printf ("Error: no tag was found\n");
00446       nfc_disconnect (pnd);
00447       exit (EXIT_FAILURE);
00448     }
00449     // Test if we are dealing with a MIFARE compatible tag
00450     if ((nti.nai.btSak & 0x08) == 0) {
00451       printf ("Error: tag is not a MIFARE Classic card\n");
00452       nfc_disconnect (pnd);
00453       exit (EXIT_FAILURE);
00454     }
00455 
00456     if (bUseKeyFile) {
00457       // Get the info from the key dump
00458       b4K = (mtKeys.amb[0].mbm.abtATQA[1] == 0x02);
00459       pbtUID = mtKeys.amb[0].mbm.abtUID;
00460 
00461       // Compare if key dump UID is the same as the current tag UID
00462       if (memcmp (nti.nai.abtUid, pbtUID, 4) != 0) {
00463         printf ("Expected MIFARE Classic %ck card with UID: %02x%02x%02x%02x\n", b4K ? '4' : '1', pbtUID[3], pbtUID[2],
00464                 pbtUID[1], pbtUID[0]);
00465       }
00466     }
00467     // Get the info from the current tag
00468     pbtUID = nti.nai.abtUid;
00469     b4K = (nti.nai.abtAtqa[1] == 0x02);
00470     printf ("Found MIFARE Classic %ck card with UID: %02x%02x%02x%02x\n", b4K ? '4' : '1', pbtUID[3], pbtUID[2],
00471             pbtUID[1], pbtUID[0]);
00472 
00473     uiBlocks = (b4K) ? 0xff : 0x3f;
00474 
00475     if (atAction == ACTION_READ) {
00476       if (read_card ()) {
00477         printf ("Writing data to file: %s ...", argv[3]);
00478         fflush (stdout);
00479         pfDump = fopen (argv[3], "wb");
00480         if (fwrite (&mtDump, 1, sizeof (mtDump), pfDump) != sizeof (mtDump)) {
00481           printf ("\nCould not write to file: %s\n", argv[3]);
00482           exit (EXIT_FAILURE);
00483         }
00484         printf ("Done.\n");
00485         fclose (pfDump);
00486       }
00487     } else {
00488       write_card ();
00489     }
00490 
00491     nfc_disconnect (pnd);
00492     break;
00493 
00494   case ACTION_EXTRACT:{
00495       const char *pcDump = argv[2];
00496       const char *pcPayload = argv[3];
00497 
00498       FILE   *pfDump = NULL;
00499       FILE   *pfPayload = NULL;
00500 
00501       char    abDump[4096];
00502       char    abPayload[4096];
00503 
00504       pfDump = fopen (pcDump, "rb");
00505 
00506       if (pfDump == NULL) {
00507         printf ("Could not open dump file: %s\n", pcDump);
00508         exit (EXIT_FAILURE);
00509       }
00510 
00511       if (fread (abDump, 1, sizeof (abDump), pfDump) != sizeof (abDump)) {
00512         printf ("Could not read dump file: %s\n", pcDump);
00513         fclose (pfDump);
00514         exit (EXIT_FAILURE);
00515       }
00516       fclose (pfDump);
00517 
00518       mifare_classic_extract_payload (abDump, abPayload);
00519 
00520       printf ("Writing data to file: %s\n", pcPayload);
00521       pfPayload = fopen (pcPayload, "wb");
00522       if (fwrite (abPayload, 1, sizeof (abPayload), pfPayload) != sizeof (abPayload)) {
00523         printf ("Could not write to file: %s\n", pcPayload);
00524         exit (EXIT_FAILURE);
00525       }
00526       fclose (pfPayload);
00527       printf ("Done, all bytes have been extracted!\n");
00528     }
00529   };
00530 
00531   exit (EXIT_SUCCESS);
00532 }