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