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 "mifaretag.h"
00041 #include "bitutils.h"
00042 
00043 static nfc_device_t* pnd;
00044 static nfc_target_info_t nti;
00045 static mifare_param mp;
00046 static mifare_tag mtKeys;
00047 static mifare_tag mtDump;
00048 static bool bUseKeyA;
00049 static bool bUseKeyFile;
00050 static uint32_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 static size_t num_keys = sizeof(keys) / 6;
00062 
00063 void print_success_or_failure(bool bFailure, uint32_t* uiBlockCounter)
00064 {
00065   printf("%c",(bFailure)?'x':'.');
00066   if (uiBlockCounter)
00067     *uiBlockCounter += (bFailure)?0:4;
00068 }
00069 
00070 bool is_first_block(uint32_t uiBlock)
00071 {
00072   // Test if we are in the small or big sectors
00073   if (uiBlock < 128) return ((uiBlock)%4 == 0); else return ((uiBlock)%16 == 0);
00074 }
00075 
00076 bool is_trailer_block(uint32_t uiBlock)
00077 {
00078   // Test if we are in the small or big sectors
00079   if (uiBlock < 128) return ((uiBlock+1)%4 == 0); else return ((uiBlock+1)%16 == 0);
00080 }
00081 
00082 uint32_t get_trailer_block(uint32_t uiFirstBlock)
00083 {
00084   // Test if we are in the small or big sectors
00085   uint32_t trailer_block = 0;
00086   if (uiFirstBlock < 128) {
00087     trailer_block = uiFirstBlock + (3 - (uiFirstBlock % 4));
00088   } else {
00089     trailer_block = uiFirstBlock + (15 - (uiFirstBlock % 16));
00090   }
00091   return trailer_block;
00092 }
00093 
00094 bool authenticate(uint32_t uiBlock)
00095 {
00096   mifare_cmd mc;
00097   uint32_t uiTrailerBlock;
00098   size_t key_index;
00099   
00100   // Key file authentication.
00101   if (bUseKeyFile)
00102   {
00103     // Set the authentication information (uid)
00104     memcpy(mp.mpa.abtUid,nti.nai.abtUid,4);
00105 
00106     // Locate the trailer (with the keys) used for this sector
00107     uiTrailerBlock = get_trailer_block(uiBlock);
00108       
00109     // Determin if we should use the a or the b key
00110     if (bUseKeyA)
00111     {
00112       mc = MC_AUTH_A;
00113       memcpy(mp.mpa.abtKey,mtKeys.amb[uiTrailerBlock].mbt.abtKeyA,6);
00114     } else {
00115       mc = MC_AUTH_B;
00116       memcpy(mp.mpa.abtKey,mtKeys.amb[uiTrailerBlock].mbt.abtKeyB,6);
00117     }
00118 
00119     // Try to authenticate for the current sector
00120     if (nfc_initiator_mifare_cmd(pnd,mc,uiBlock,&mp))
00121         return true;
00122   }
00123   
00124   // Auto authentication.
00125   else
00126   {
00127     // Determin if we should use the a or the b key
00128     mc = (bUseKeyA) ? MC_AUTH_A : MC_AUTH_B;
00129       
00130     // Set the authentication information (uid)
00131     memcpy(mp.mpa.abtUid,nti.nai.abtUid,4);
00132     
00133     for (key_index = 0; key_index < num_keys; key_index++)
00134     {
00135       memcpy(mp.mpa.abtKey, keys + (key_index*6), 6);
00136       if (nfc_initiator_mifare_cmd(pnd, mc, uiBlock, &mp))
00137       {
00141         if (bUseKeyA)
00142             memcpy(mtKeys.amb[uiBlock].mbt.abtKeyA,&mp.mpa.abtKey,6);
00143         else
00144             memcpy(mtKeys.amb[uiBlock].mbt.abtKeyB,&mp.mpa.abtKey,6);
00145         
00146         return true;
00147       }
00148     
00149       nfc_initiator_select_tag(pnd, NM_ISO14443A_106, mp.mpa.abtUid, 4, NULL);
00150     }
00151   }
00152   
00153   return false;
00154 }
00155 
00156 bool read_card()
00157 {
00158   int32_t iBlock;
00159   bool bFailure = false;
00160   uint32_t uiReadBlocks = 0;
00161 
00162   printf("Reading out %d blocks |",uiBlocks+1);
00163 
00164   // Read the card from end to begin
00165   for (iBlock=uiBlocks; iBlock>=0; iBlock--)
00166   {
00167     // Authenticate everytime we reach a trailer block
00168     if (is_trailer_block(iBlock))
00169     {
00170       // Skip this the first time, bFailure it means nothing (yet)
00171       if (iBlock != uiBlocks)
00172         print_success_or_failure(bFailure, &uiReadBlocks);
00173 
00174       // Show if the readout went well
00175       if (bFailure)
00176       {
00177         // When a failure occured we need to redo the anti-collision
00178         if (!nfc_initiator_select_tag(pnd,NM_ISO14443A_106,NULL,0,&nti))
00179         {
00180           printf("!\nError: tag was removed\n");
00181           return 1;
00182         }
00183         bFailure = false;
00184       }
00185 
00186       fflush(stdout);
00187       
00188       // Try to authenticate for the current sector
00189       if (!authenticate(iBlock))
00190       {
00191         printf("!\nError: authentication failed for block %02x\n",iBlock);
00192         return false;
00193       }
00194 
00195       // Try to read out the trailer
00196       if (nfc_initiator_mifare_cmd(pnd,MC_READ,iBlock,&mp))
00197       {
00198         // Copy the keys over from our key dump and store the retrieved access bits
00199         memcpy(mtDump.amb[iBlock].mbt.abtKeyA,mtKeys.amb[iBlock].mbt.abtKeyA,6);
00200         memcpy(mtDump.amb[iBlock].mbt.abtAccessBits,mp.mpd.abtData+6,4);
00201         memcpy(mtDump.amb[iBlock].mbt.abtKeyB,mtKeys.amb[iBlock].mbt.abtKeyB,6);
00202       }
00203     } else {
00204       // Make sure a earlier readout did not fail
00205       if (!bFailure)
00206       {
00207         // Try to read out the data block
00208         if (nfc_initiator_mifare_cmd(pnd,MC_READ,iBlock,&mp))
00209         {
00210           memcpy(mtDump.amb[iBlock].mbd.abtData,mp.mpd.abtData,16);
00211         } else {
00212           bFailure = true;
00213         }
00214       }
00215     }
00216   }
00217   print_success_or_failure(bFailure, &uiReadBlocks);
00218   printf("|\n");
00219   printf("Done, %d of %d blocks read.\n", uiReadBlocks, uiBlocks+1);
00220   fflush(stdout);
00221 
00222   return true;
00223 }
00224 
00225 bool write_card()
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   {
00236     // Authenticate everytime we reach the first sector of a new block
00237     if (is_first_block(uiBlock))
00238     {
00239       // Skip this the first time, bFailure it means nothing (yet)
00240       if (uiBlock != uiBlocks)
00241         print_success_or_failure(bFailure, &uiWriteBlocks);
00242 
00243       // Show if the readout went well
00244       if (bFailure)
00245       {
00246         // When a failure occured we need to redo the anti-collision
00247         if (!nfc_initiator_select_tag(pnd,NM_ISO14443A_106,NULL,0,&nti))
00248         {
00249           printf("!\nError: tag was removed\n");
00250           return false;
00251         }
00252         bFailure = false;
00253       }
00254 
00255       fflush(stdout);
00256 
00257       // Try to authenticate for the current sector
00258       if (!authenticate(uiBlock))
00259       { 
00260         printf("!\nError: authentication failed for block %02x\n",uiBlock);
00261         return false;
00262       }
00263     }
00264 
00265     if (is_trailer_block(uiBlock))
00266     {
00267       // Copy the keys over from our key dump and store the retrieved access bits
00268       memcpy(mp.mpd.abtData,mtDump.amb[uiBlock].mbt.abtKeyA,6);
00269       memcpy(mp.mpd.abtData+6,mtDump.amb[uiBlock].mbt.abtAccessBits,4);
00270       memcpy(mp.mpd.abtData+10,mtDump.amb[uiBlock].mbt.abtKeyB,6);
00271 
00272       // Try to write the trailer
00273       if (nfc_initiator_mifare_cmd(pnd,MC_WRITE,uiBlock,&mp) == false) {
00274         printf("failed to write trailer block %d \n", uiBlock);
00275         bFailure = true;
00276       }
00277     } else {
00278 
00279       // The first block 0x00 is read only, skip this
00280       if (uiBlock == 0) continue;
00281 
00282       // Make sure a earlier write did not fail
00283       if (!bFailure)
00284       {
00285         // Try to write the data block
00286         memcpy(mp.mpd.abtData,mtDump.amb[uiBlock].mbd.abtData,16);
00287         if (!nfc_initiator_mifare_cmd(pnd,MC_WRITE,uiBlock,&mp)) bFailure = true;
00288       }
00289     }
00290   }
00291   print_success_or_failure(bFailure, &uiWriteBlocks);
00292   printf("|\n");
00293   printf("Done, %d of %d blocks written.\n", uiWriteBlocks, uiBlocks+1);
00294   fflush(stdout);
00295 
00296   return true;
00297 }
00298 
00299 void mifare_classic_extract_payload(const char* abDump, char* pbPayload)
00300 {
00301   uint8_t uiSectorIndex;
00302   uint8_t uiBlockIndex;
00303   size_t szDumpOffset;
00304   size_t szPayloadIndex = 0;
00305 
00306   for(uiSectorIndex=1; uiSectorIndex<16; uiSectorIndex++) {
00307     for(uiBlockIndex=0; uiBlockIndex<3; uiBlockIndex++) {
00308       szDumpOffset = uiSectorIndex*16*4 + uiBlockIndex*16;
00309 //      for(uint8_t uiByteIndex=0; uiByteIndex<16; uiByteIndex++) printf("%02x ", abDump[szPayloadIndex+uiByteIndex]);
00310       memcpy(pbPayload+szPayloadIndex, abDump+szDumpOffset, 16);
00311       szPayloadIndex += 16;
00312     }
00313   }
00314 }
00315 
00316 typedef enum {
00317   ACTION_READ,
00318   ACTION_WRITE,
00319   ACTION_EXTRACT,
00320   ACTION_USAGE
00321 } action_t;
00322 
00323 void print_usage(const char* pcProgramName)
00324 {
00325   printf("Usage: ");
00326   printf("%s r|w a|b <dump.mfd> [<keys.mfd>]\n", pcProgramName);
00327   printf("  r|w           - Perform read from (r) or write to (w) card\n");
00328   printf("  a|b           - Use A or B keys for action\n");
00329   printf("  <dump.mfd>    - MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)\n");
00330   printf("  <keys.mfd>    - MiFare Dump (MFD) that contain the keys (optional)\n");
00331   printf("Or: ");
00332   printf("%s x <dump.mfd> <payload.bin>\n", pcProgramName);
00333   printf("  x             - Extract payload (data blocks) from MFD\n");
00334   printf("  <dump.mfd>    - MiFare Dump (MFD) that contains wanted payload\n");
00335   printf("  <payload.bin> - Binary file where payload will be extracted\n");
00336 }
00337 
00338 int main(int argc, const char* argv[])
00339 {
00340   bool b4K;
00341   action_t atAction = ACTION_USAGE;
00342   byte_t* pbtUID;
00343   FILE* pfKeys = NULL;
00344   FILE* pfDump = NULL;
00345   const char* command = argv[1];
00346 
00347   if(argc < 2)
00348   {
00349     print_usage(argv[0]);
00350     exit(EXIT_FAILURE);
00351   }
00352 
00353   if(strcmp(command, "r") == 0)
00354   {
00355     atAction = ACTION_READ;
00356     bUseKeyA = tolower((int)((unsigned char)*(argv[2]))) == 'a';
00357     bUseKeyFile = (argc > 4);
00358   } else if(strcmp(command, "w") == 0) 
00359   {
00360     atAction = ACTION_WRITE;
00361     bUseKeyA = tolower((int)((unsigned char)*(argv[2]))) == 'a';
00362     bUseKeyFile = (argc > 4);
00363   } else if(strcmp(command, "x") == 0)
00364   {
00365     atAction = ACTION_EXTRACT;
00366   }
00367 
00368   switch(atAction) {
00369     case ACTION_USAGE:
00370       print_usage(argv[0]);
00371       exit(EXIT_FAILURE);
00372       break;
00373     case ACTION_READ:
00374     case ACTION_WRITE:
00375       if (argc < 4)
00376       {
00377         print_usage(argv[0]);
00378         exit(EXIT_FAILURE);
00379       }
00380 
00381       if (bUseKeyFile)
00382       {
00383         pfKeys = fopen(argv[4],"rb");
00384         if (pfKeys == NULL)
00385         {
00386           printf("Could not open keys file: %s\n",argv[4]);
00387           exit(EXIT_FAILURE);
00388         }
00389         if (fread(&mtKeys,1,sizeof(mtKeys),pfKeys) != sizeof(mtKeys))
00390         {
00391           printf("Could not read keys file: %s\n",argv[4]);
00392           fclose(pfKeys);
00393           exit(EXIT_FAILURE);
00394         }
00395         fclose(pfKeys);
00396       }
00397     
00398       if(atAction == ACTION_READ) {
00399         memset(&mtDump,0x00,sizeof(mtDump));
00400       } else {
00401         pfDump = fopen(argv[3],"rb");
00402     
00403         if (pfDump == NULL)
00404         {
00405           printf("Could not open dump file: %s\n",argv[3]);
00406           exit(EXIT_FAILURE);
00407         }
00408     
00409         if (fread(&mtDump,1,sizeof(mtDump),pfDump) != sizeof(mtDump))
00410         {
00411           printf("Could not read dump file: %s\n",argv[3]);
00412           fclose(pfDump);
00413           exit(EXIT_FAILURE);
00414         }
00415         fclose(pfDump);
00416       }
00417       // printf("Successfully opened required files\n");
00418     
00419       // Try to open the NFC reader
00420       pnd = nfc_connect(NULL);
00421       if (pnd == NULL)
00422       {
00423         printf("Error connecting NFC reader\n");
00424         exit(EXIT_FAILURE);
00425       }
00426     
00427       nfc_initiator_init(pnd);
00428     
00429       // Drop the field for a while
00430       nfc_configure(pnd,NDO_ACTIVATE_FIELD,false);
00431     
00432       // Let the reader only try once to find a tag
00433       nfc_configure(pnd,NDO_INFINITE_SELECT,false);
00434       nfc_configure(pnd,NDO_HANDLE_CRC,true);
00435       nfc_configure(pnd,NDO_HANDLE_PARITY,true);
00436     
00437       // Enable field so more power consuming cards can power themselves up
00438       nfc_configure(pnd,NDO_ACTIVATE_FIELD,true);
00439     
00440       printf("Connected to NFC reader: %s\n",pnd->acName);
00441     
00442       // Try to find a MIFARE Classic tag
00443       if (!nfc_initiator_select_tag(pnd,NM_ISO14443A_106,NULL,0,&nti))
00444       {
00445         printf("Error: no tag was found\n");
00446         nfc_disconnect(pnd);
00447         exit(EXIT_FAILURE);
00448       }
00449     
00450       // Test if we are dealing with a MIFARE compatible tag
00451       if ((nti.nai.btSak & 0x08) == 0)
00452       {
00453         printf("Error: tag is not a MIFARE Classic card\n");
00454         nfc_disconnect(pnd);
00455         exit(EXIT_FAILURE);
00456       }
00457     
00458       if (bUseKeyFile)
00459       {
00460         // Get the info from the key dump
00461         b4K = (mtKeys.amb[0].mbm.abtATQA[1] == 0x02);
00462         pbtUID = mtKeys.amb[0].mbm.abtUID;
00463     
00464         // Compare if key dump UID is the same as the current tag UID
00465         if (memcmp(nti.nai.abtUid,pbtUID,4) != 0)
00466         {
00467           printf("Expected MIFARE Classic %cK card with UID: %08x\n",b4K?'4':'1',swap_endian32(pbtUID));
00468         }
00469       }
00470     
00471       // Get the info from the current tag
00472       pbtUID = nti.nai.abtUid;
00473       b4K = (nti.nai.abtAtqa[1] == 0x02);
00474       printf("Found MIFARE Classic %cK card with UID: %08x\n",b4K?'4':'1',swap_endian32(pbtUID));
00475     
00476       uiBlocks = (b4K)?0xff:0x3f;
00477     
00478       if (atAction == ACTION_READ)
00479       {
00480         if (read_card())
00481         {
00482           printf("Writing data to file: %s ... ",argv[3]);
00483           fflush(stdout);
00484           pfDump = fopen(argv[3],"wb");
00485           if (fwrite(&mtDump,1,sizeof(mtDump),pfDump) != sizeof(mtDump))
00486           {
00487             printf("\nCould not write to file: %s\n",argv[3]);
00488             exit(EXIT_FAILURE);
00489           }
00490           printf("Done.\n");
00491           fclose(pfDump);
00492         }
00493       } else {
00494         write_card();
00495       }
00496     
00497       nfc_disconnect(pnd);
00498       break;
00499 
00500     case ACTION_EXTRACT: {
00501       const char* pcDump = argv[2];
00502       const char* pcPayload = argv[3];
00503 
00504       FILE* pfDump = NULL;
00505       FILE* pfPayload = NULL;
00506       
00507       char abDump[4096];
00508       char abPayload[4096];
00509 
00510       pfDump = fopen(pcDump,"rb");
00511     
00512       if (pfDump == NULL)
00513       {
00514         printf("Could not open dump file: %s\n",pcDump);
00515         exit(EXIT_FAILURE);
00516       }
00517     
00518       if (fread(abDump,1,sizeof(abDump),pfDump) != sizeof(abDump))
00519       {
00520         printf("Could not read dump file: %s\n",pcDump);
00521         fclose(pfDump);
00522         exit(EXIT_FAILURE);
00523       }
00524       fclose(pfDump);
00525 
00526       mifare_classic_extract_payload(abDump, abPayload);
00527 
00528       printf("Writing data to file: %s\n",pcPayload);
00529       pfPayload = fopen(pcPayload,"wb");
00530       if (fwrite(abPayload,1,sizeof(abPayload),pfPayload) != sizeof(abPayload))
00531       {
00532         printf("Could not write to file: %s\n",pcPayload);
00533         exit(EXIT_FAILURE);
00534       }
00535       fclose(pfPayload);
00536       printf("Done, all bytes have been extracted!\n");
00537     }
00538   };
00539 
00540   exit(EXIT_SUCCESS);
00541 }
00542