nfc-mfultralight.c

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 "mifareultag.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 mifareul_tag mtDump;
00047 static uint32_t uiBlocks = 0xF;
00048 
00049 void print_success_or_failure(bool bFailure, uint32_t* uiBlockCounter)
00050 {
00051   printf("%c",(bFailure)?'x':'.');
00052   if (uiBlockCounter)
00053     *uiBlockCounter += (bFailure)?0:1;
00054 }
00055 
00056 bool read_card()
00057 {
00058   uint32_t page;
00059   bool bFailure = false;
00060   uint32_t uiReadBlocks = 0;
00061 
00062   printf("Reading out %d blocks |",uiBlocks+1);
00063 
00064   for (page = 0; page <= uiBlocks;  page += 4)
00065   {
00066       // Skip this the first time, bFailure it means nothing (yet)
00067       if (page != 0)
00068       {
00069         print_success_or_failure(bFailure, &uiReadBlocks);
00070         print_success_or_failure(bFailure, &uiReadBlocks);
00071         print_success_or_failure(bFailure, &uiReadBlocks);
00072         print_success_or_failure(bFailure, &uiReadBlocks);
00073       }
00074 
00075       // Try to read out the data block
00076       if (nfc_initiator_mifare_cmd(pnd,MC_READ,page,&mp))
00077       {
00078         memcpy(mtDump.amb[page / 4].mbd.abtData, mp.mpd.abtData, 16);
00079       } else {
00080         bFailure = true;
00081         break;
00082       }
00083   }
00084   //return bSuccess;
00085   print_success_or_failure(bFailure, &uiReadBlocks);
00086   printf("|\n");
00087   printf("Done, %d of %d blocks read.\n", uiReadBlocks, uiBlocks+1);
00088   fflush(stdout);
00089 
00090   return (!bFailure);
00091 }
00092 
00093 bool write_card()
00094 {
00095   uint32_t uiBlock = 0;
00096   int page;
00097   bool bFailure = false;
00098   uint32_t uiWriteBlocks = 0;
00099 
00100   for (page = 0x4; page <= 0xF; page++) {
00101       // Show if the readout went well
00102       if (bFailure)
00103       {
00104         printf("x");
00105         // When a failure occured we need to redo the anti-collision
00106         if (!nfc_initiator_select_tag(pnd,NM_ISO14443A_106,NULL,0,&nti))
00107         {
00108           printf("!\nError: tag was removed\n");
00109           return false;
00110         }
00111         bFailure = false;
00112       }
00113       fflush(stdout);
00114 
00115       // Make sure a earlier write did not fail
00116       if (!bFailure)
00117       {
00118         // For the Mifare Ultralight, this write command can be used
00119         // in compatibility mode, which only actually writes the first 
00120         // page (4 bytes). The Ultralight-specific Write command only
00121         // writes one page at a time.
00122         uiBlock = page / 4;
00123         memcpy(mp.mpd.abtData, mtDump.amb[uiBlock].mbd.abtData + ((page % 4) * 4), 16);
00124         if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, page, &mp)) bFailure = true;
00125       }
00126   }
00127   print_success_or_failure(bFailure, &uiWriteBlocks);
00128   printf("|\n");
00129   printf("Done, %d of %d blocks written.\n", uiWriteBlocks, uiBlocks+1);
00130   fflush(stdout);
00131 
00132   return true;
00133 }
00134 
00135 int main(int argc, const char* argv[])
00136 {     
00137   bool bReadAction;
00138   byte_t* pbtUID;
00139   FILE* pfDump;
00140 
00141   if (argc < 3)
00142   {
00143     printf("\n");
00144     printf("%s r|w <dump.mfd>\n", argv[0]);
00145     printf("\n");
00146     printf("r|w         - Perform read from or write to card\n");
00147     printf("<dump.mfd>  - MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)\n");
00148     printf("\n");
00149     return 1;
00150   }
00151 
00152   printf("\nChecking arguments and settings\n");
00153 
00154   bReadAction = tolower((int)((unsigned char)*(argv[1])) == 'r');
00155 
00156   if (bReadAction)
00157   {
00158     memset(&mtDump,0x00,sizeof(mtDump));
00159   } else {
00160     pfDump = fopen(argv[2],"rb");
00161 
00162     if (pfDump == NULL)
00163     {
00164       printf("Could not open dump file: %s\n",argv[2]);
00165       return 1;
00166     }
00167 
00168     if (fread(&mtDump,1,sizeof(mtDump),pfDump) != sizeof(mtDump))
00169     {
00170       printf("Could not read from dump file: %s\n",argv[2]);
00171       fclose(pfDump);
00172       return 1;
00173     }
00174     fclose(pfDump);
00175   }
00176   printf("Succesful opened the dump file\n");
00177 
00178   // Try to open the NFC reader
00179   pnd = nfc_connect(NULL);
00180   if (pnd == NULL)
00181   {
00182     printf("Error connecting NFC reader\n");
00183     return 1;
00184   }
00185 
00186   nfc_initiator_init(pnd);
00187 
00188   // Drop the field for a while
00189   nfc_configure(pnd,NDO_ACTIVATE_FIELD,false);
00190 
00191   // Let the reader only try once to find a tag
00192   nfc_configure(pnd,NDO_INFINITE_SELECT,false);
00193   nfc_configure(pnd,NDO_HANDLE_CRC,true);
00194   nfc_configure(pnd,NDO_HANDLE_PARITY,true);
00195 
00196   // Enable field so more power consuming cards can power themselves up
00197   nfc_configure(pnd,NDO_ACTIVATE_FIELD,true);
00198 
00199   printf("Connected to NFC reader: %s\n",pnd->acName);
00200 
00201   // Try to find a MIFARE Ultralight tag
00202   if (!nfc_initiator_select_tag(pnd,NM_ISO14443A_106,NULL,0,&nti))
00203   {
00204     printf("Error: no tag was found\n");
00205     nfc_disconnect(pnd);
00206     return 1;
00207   }
00208 
00209   // Test if we are dealing with a MIFARE compatible tag
00210 
00211   if (nti.nai.abtAtqa[1] != 0x44){
00212       printf("Error: tag is not a MIFARE Ultralight card\n");
00213     nfc_disconnect(pnd);
00214       return 1;
00215   }
00216 
00217 
00218   // Get the info from the current tag
00219   pbtUID = nti.nai.abtUid;
00220   printf("Found MIFARE Ultralight card with uid: %08x\n", swap_endian32(pbtUID));
00221 
00222   if (bReadAction)
00223   {
00224     if (read_card())
00225     {
00226       printf("Writing data to file: %s ... ",argv[2]);
00227       fflush(stdout);
00228       pfDump = fopen(argv[2],"wb");
00229       if (pfDump == NULL)
00230       {
00231         printf("Could not open file: %s\n",argv[2]);
00232         return 1;
00233       }
00234       if (fwrite(&mtDump,1,sizeof(mtDump),pfDump) != sizeof(mtDump))
00235       {
00236         printf("Could not write to file: %s\n",argv[2]);
00237         return 1;
00238       }
00239       fclose(pfDump);
00240       printf("Done.\n");
00241     }
00242   } else {
00243     write_card();
00244   }
00245 
00246   nfc_disconnect(pnd);
00247 
00248   return 0;
00249 }