nfc-mfultralight.c

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