nfc-mfclassic.c 17.9 KB
Newer Older
1
/*-
2
 * Public platform independent Near Field Communication (NFC) library examples
3
 *
4 5 6 7
 * Copyright (C) 2009 Roel Verdult
 * Copyright (C) 2010 Romain Tartière
 * Copyright (C) 2010 Romuald Conty
 * Copyright (C) 2011 Adam Laurie
8
 *
9 10 11
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *  1) Redistributions of source code must retain the above copyright notice,
12
 *  this list of conditions and the following disclaimer.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *  2 )Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
28
 *
29
 * Note that this license only applies on the examples, NFC library itself is under LGPL
30
 *
31 32 33 34 35
 */

/**
 * @file nfc-mfclassic.c
 * @brief MIFARE Classic manipulation example
36
 */
Roel Verdult's avatar
Roel Verdult committed
37

38
#ifdef HAVE_CONFIG_H
39
#  include "config.h"
40 41
#endif // HAVE_CONFIG_H

Roel Verdult's avatar
Roel Verdult committed
42 43
#include <stdio.h>
#include <stdlib.h>
44
#include <stdint.h>
45
#include <stddef.h>
46
#include <stdbool.h>
47

Roel Verdult's avatar
Roel Verdult committed
48 49 50
#include <string.h>
#include <ctype.h>

51
#include <nfc/nfc.h>
52

53
#include "mifare.h"
54
#include "nfc-utils.h"
Roel Verdult's avatar
Roel Verdult committed
55

56
static nfc_context *context;
57 58
static nfc_device *pnd;
static nfc_target nt;
Roel Verdult's avatar
Roel Verdult committed
59
static mifare_param mp;
60 61
static mifare_classic_tag mtKeys;
static mifare_classic_tag mtDump;
Roel Verdult's avatar
Roel Verdult committed
62
static bool bUseKeyA;
Roel Verdult's avatar
Roel Verdult committed
63
static bool bUseKeyFile;
64
static uint8_t uiBlocks;
65
static uint8_t keys[] = {
66 67 68 69 70 71 72
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7,
  0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
  0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
  0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd,
  0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a,
  0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
Roel Verdult's avatar
Roel Verdult committed
73
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Roel Verdult's avatar
Roel Verdult committed
74
  0xab, 0xcd, 0xef, 0x12, 0x34, 0x56
Roel Verdult's avatar
Roel Verdult committed
75
};
Roel Verdult's avatar
Roel Verdult committed
76

77
static const nfc_modulation nmMifare = {
78 79 80 81
  .nmt = NMT_ISO14443A,
  .nbr = NBR_106,
};

82
static size_t num_keys = sizeof(keys) / 6;
83

84 85
#define MAX_FRAME_LEN 264

86
static uint8_t abtRx[MAX_FRAME_LEN];
87
static int szRxBits;
88

89
uint8_t  abtHalt[4] = { 0x50, 0x00, 0x00, 0x00 };
90 91

// special unlock command
92 93
uint8_t  abtUnlock1[1] = { 0x40 };
uint8_t  abtUnlock2[1] = { 0x43 };
94 95

static  bool
96
transmit_bits(const uint8_t *pbtTx, const size_t szTxBits)
97 98
{
  // Show transmitted command
99 100
  printf("Sent bits:     ");
  print_hex_bits(pbtTx, szTxBits);
101
  // Transmit the bit frame command, we don't use the arbitrary parity feature
102
  if ((szRxBits = nfc_initiator_transceive_bits(pnd, pbtTx, szTxBits, NULL, abtRx, sizeof(abtRx), NULL)) < 0)
103 104 105
    return false;

  // Show received answer
106 107
  printf("Received bits: ");
  print_hex_bits(abtRx, szRxBits);
108 109 110 111 112 113
  // Succesful transfer
  return true;
}


static  bool
114
transmit_bytes(const uint8_t *pbtTx, const size_t szTx)
115 116
{
  // Show transmitted command
117 118
  printf("Sent bits:     ");
  print_hex(pbtTx, szTx);
119
  // Transmit the command bytes
120
  int res;
121
  if ((res = nfc_initiator_transceive_bytes(pnd, pbtTx, szTx, abtRx, sizeof(abtRx), 0)) < 0)
122 123 124
    return false;

  // Show received answer
125 126
  printf("Received bits: ");
  print_hex(abtRx, res);
127 128 129 130
  // Succesful transfer
  return true;
}

131
static void
132
print_success_or_failure(bool bFailure, uint32_t *uiBlockCounter)
133
{
134
  printf("%c", (bFailure) ? 'x' : '.');
135 136
  if (uiBlockCounter && !bFailure)
    *uiBlockCounter += (*uiBlockCounter < 128) ? 4 : 16;
137 138
}

139
static  bool
140
is_first_block(uint32_t uiBlock)
Roel Verdult's avatar
Roel Verdult committed
141 142
{
  // Test if we are in the small or big sectors
143 144 145 146
  if (uiBlock < 128)
    return ((uiBlock) % 4 == 0);
  else
    return ((uiBlock) % 16 == 0);
Roel Verdult's avatar
Roel Verdult committed
147 148
}

149
static  bool
150
is_trailer_block(uint32_t uiBlock)
Roel Verdult's avatar
Roel Verdult committed
151 152
{
  // Test if we are in the small or big sectors
153 154 155 156
  if (uiBlock < 128)
    return ((uiBlock + 1) % 4 == 0);
  else
    return ((uiBlock + 1) % 16 == 0);
Roel Verdult's avatar
Roel Verdult committed
157 158
}

159
static  uint32_t
160
get_trailer_block(uint32_t uiFirstBlock)
Roel Verdult's avatar
Roel Verdult committed
161 162
{
  // Test if we are in the small or big sectors
163 164 165 166 167 168 169
  uint32_t trailer_block = 0;
  if (uiFirstBlock < 128) {
    trailer_block = uiFirstBlock + (3 - (uiFirstBlock % 4));
  } else {
    trailer_block = uiFirstBlock + (15 - (uiFirstBlock % 16));
  }
  return trailer_block;
Roel Verdult's avatar
Roel Verdult committed
170 171
}

172
static  bool
173
authenticate(uint32_t uiBlock)
Roel Verdult's avatar
Roel Verdult committed
174 175 176
{
  mifare_cmd mc;
  uint32_t uiTrailerBlock;
177
  size_t  key_index;
178

179
  // Set the authentication information (uid)
180
  memcpy(mp.mpa.abtAuthUid, nt.nti.nai.abtUid + nt.nti.nai.szUidLen - 4, 4);
181 182 183 184

  // Should we use key A or B?
  mc = (bUseKeyA) ? MC_AUTH_A : MC_AUTH_B;

Roel Verdult's avatar
Roel Verdult committed
185
  // Key file authentication.
186
  if (bUseKeyFile) {
Roel Verdult's avatar
Roel Verdult committed
187 188

    // Locate the trailer (with the keys) used for this sector
189
    uiTrailerBlock = get_trailer_block(uiBlock);
190

191 192
    // Extract the right key from dump file
    if (bUseKeyA)
193
      memcpy(mp.mpa.abtKey, mtKeys.amb[uiTrailerBlock].mbt.abtKeyA, 6);
194
    else
195
      memcpy(mp.mpa.abtKey, mtKeys.amb[uiTrailerBlock].mbt.abtKeyB, 6);
Roel Verdult's avatar
Roel Verdult committed
196 197

    // Try to authenticate for the current sector
198
    if (nfc_initiator_mifare_cmd(pnd, mc, uiBlock, &mp))
199
      return true;
200 201
  } else {
    // Try to guess the right key
202
    for (key_index = 0; key_index < num_keys; key_index++) {
203 204
      memcpy(mp.mpa.abtKey, keys + (key_index * 6), 6);
      if (nfc_initiator_mifare_cmd(pnd, mc, uiBlock, &mp)) {
Roel Verdult's avatar
Roel Verdult committed
205
        if (bUseKeyA)
206
          memcpy(mtKeys.amb[uiBlock].mbt.abtKeyA, &mp.mpa.abtKey, 6);
Roel Verdult's avatar
Roel Verdult committed
207
        else
208
          memcpy(mtKeys.amb[uiBlock].mbt.abtKeyB, &mp.mpa.abtKey, 6);
Roel Verdult's avatar
Roel Verdult committed
209 210
        return true;
      }
211
      nfc_initiator_select_passive_target(pnd, nmMifare, nt.nti.nai.abtUid, nt.nti.nai.szUidLen, NULL);
Roel Verdult's avatar
Roel Verdult committed
212 213
    }
  }
214

Roel Verdult's avatar
Roel Verdult committed
215 216 217
  return false;
}

218
static bool
219
unlock_card(void)
220
{
221
  printf("Unlocking card\n");
222 223

  // Configure the CRC
224 225 226
  if (nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, false) < 0) {
    nfc_perror(pnd, "nfc_configure");
    exit(EXIT_FAILURE);
227 228
  }
  // Use raw send/receive methods
229 230 231
  if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false) < 0) {
    nfc_perror(pnd, "nfc_configure");
    exit(EXIT_FAILURE);
232 233 234
  }

  iso14443a_crc_append(abtHalt, 2);
235
  transmit_bytes(abtHalt, 4);
236
  // now send unlock
237
  if (!transmit_bits(abtUnlock1, 7)) {
238 239 240
    printf("unlock failure!\n");
    return false;
  }
241
  if (!transmit_bytes(abtUnlock2, 1)) {
242 243 244 245 246 247
    printf("unlock failure!\n");
    return false;
  }

  // reset reader
  // Configure the CRC
248 249 250
  if (nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, true) < 0) {
    nfc_perror(pnd, "nfc_device_set_property_bool");
    exit(EXIT_FAILURE);
251 252
  }
  // Switch off raw send/receive methods
253 254 255
  if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true) < 0) {
    nfc_perror(pnd, "nfc_device_set_property_bool");
    exit(EXIT_FAILURE);
256 257 258 259
  }
  return true;
}

260
static  bool
261
read_card(int read_unlocked)
Roel Verdult's avatar
Roel Verdult committed
262
{
263
  int32_t iBlock;
264
  bool    bFailure = false;
265
  uint32_t uiReadBlocks = 0;
Roel Verdult's avatar
Roel Verdult committed
266

267
  if (read_unlocked)
268 269 270 271
    if (!unlock_card())
      return false;


272
  printf("Reading out %d blocks |", uiBlocks + 1);
Roel Verdult's avatar
Roel Verdult committed
273 274

  // Read the card from end to begin
275
  for (iBlock = uiBlocks; iBlock >= 0; iBlock--) {
Roel Verdult's avatar
Roel Verdult committed
276
    // Authenticate everytime we reach a trailer block
277
    if (is_trailer_block(iBlock)) {
278 279
      // Skip this the first time, bFailure it means nothing (yet)
      if (iBlock != uiBlocks)
280
        print_success_or_failure(bFailure, &uiReadBlocks);
281

Roel Verdult's avatar
Roel Verdult committed
282
      // Show if the readout went well
283
      if (bFailure) {
Roel Verdult's avatar
Roel Verdult committed
284
        // When a failure occured we need to redo the anti-collision
285
        if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) {
286
          printf("!\nError: tag was removed\n");
287
          return false;
Roel Verdult's avatar
Roel Verdult committed
288 289 290
        }
        bFailure = false;
      }
291

292
      fflush(stdout);
293

Roel Verdult's avatar
Roel Verdult committed
294
      // Try to authenticate for the current sector
295 296
      if (!read_unlocked && !authenticate(iBlock)) {
        printf("!\nError: authentication failed for block 0x%02x\n", iBlock);
Roel Verdult's avatar
Roel Verdult committed
297 298 299
        return false;
      }
      // Try to read out the trailer
300 301 302
      if (nfc_initiator_mifare_cmd(pnd, MC_READ, iBlock, &mp)) {
        if (read_unlocked) {
          memcpy(mtDump.amb[iBlock].mbd.abtData, mp.mpd.abtData, 16);
303 304
        } else {
          // Copy the keys over from our key dump and store the retrieved access bits
305 306 307
          memcpy(mtDump.amb[iBlock].mbt.abtKeyA, mtKeys.amb[iBlock].mbt.abtKeyA, 6);
          memcpy(mtDump.amb[iBlock].mbt.abtAccessBits, mp.mpd.abtData + 6, 4);
          memcpy(mtDump.amb[iBlock].mbt.abtKeyB, mtKeys.amb[iBlock].mbt.abtKeyB, 6);
308
        }
309
      } else {
310
        printf("!\nError: unable to read trailer block 0x%02x\n", iBlock);
Roel Verdult's avatar
Roel Verdult committed
311 312 313
      }
    } else {
      // Make sure a earlier readout did not fail
314
      if (!bFailure) {
Roel Verdult's avatar
Roel Verdult committed
315
        // Try to read out the data block
316 317
        if (nfc_initiator_mifare_cmd(pnd, MC_READ, iBlock, &mp)) {
          memcpy(mtDump.amb[iBlock].mbd.abtData, mp.mpd.abtData, 16);
Roel Verdult's avatar
Roel Verdult committed
318 319
        } else {
          bFailure = true;
320
          printf("!\nError: unable to read block 0x%02x\n", iBlock);
321
          return false;
Roel Verdult's avatar
Roel Verdult committed
322 323 324 325
        }
      }
    }
  }
326 327 328 329
  print_success_or_failure(bFailure, &uiReadBlocks);
  printf("|\n");
  printf("Done, %d of %d blocks read.\n", uiReadBlocks, uiBlocks + 1);
  fflush(stdout);
Roel Verdult's avatar
Roel Verdult committed
330 331 332 333

  return true;
}

334
static  bool
335
write_card(int write_block_zero)
Roel Verdult's avatar
Roel Verdult committed
336
{
337
  uint32_t uiBlock;
338
  bool    bFailure = false;
339
  uint32_t uiWriteBlocks = 0;
Roel Verdult's avatar
Roel Verdult committed
340 341


342
  if (write_block_zero)
343
    if (!unlock_card())
344 345
      return false;

346
  printf("Writing %d blocks |", uiBlocks + 1);
Roel Verdult's avatar
Roel Verdult committed
347
  // Write the card from begin to end;
348
  for (uiBlock = 0; uiBlock <= uiBlocks; uiBlock++) {
Roel Verdult's avatar
Roel Verdult committed
349
    // Authenticate everytime we reach the first sector of a new block
350
    if (is_first_block(uiBlock)) {
351
      // Skip this the first time, bFailure it means nothing (yet)
352
      if (uiBlock != 0)
353
        print_success_or_failure(bFailure, &uiWriteBlocks);
354

Roel Verdult's avatar
Roel Verdult committed
355
      // Show if the readout went well
356
      if (bFailure) {
Roel Verdult's avatar
Roel Verdult committed
357
        // When a failure occured we need to redo the anti-collision
358
        if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) {
359
          printf("!\nError: tag was removed\n");
Roel Verdult's avatar
Roel Verdult committed
360 361 362 363
          return false;
        }
        bFailure = false;
      }
364

365
      fflush(stdout);
Roel Verdult's avatar
Roel Verdult committed
366 367

      // Try to authenticate for the current sector
368 369
      if (!write_block_zero && !authenticate(uiBlock)) {
        printf("!\nError: authentication failed for block %02x\n", uiBlock);
Roel Verdult's avatar
Roel Verdult committed
370 371 372
        return false;
      }
    }
373

374
    if (is_trailer_block(uiBlock)) {
Roel Verdult's avatar
Roel Verdult committed
375
      // Copy the keys over from our key dump and store the retrieved access bits
376 377 378
      memcpy(mp.mpd.abtData, mtDump.amb[uiBlock].mbt.abtKeyA, 6);
      memcpy(mp.mpd.abtData + 6, mtDump.amb[uiBlock].mbt.abtAccessBits, 4);
      memcpy(mp.mpd.abtData + 10, mtDump.amb[uiBlock].mbt.abtKeyB, 6);
Roel Verdult's avatar
Roel Verdult committed
379 380

      // Try to write the trailer
381 382
      if (nfc_initiator_mifare_cmd(pnd, MC_WRITE, uiBlock, &mp) == false) {
        printf("failed to write trailer block %d \n", uiBlock);
383 384
        bFailure = true;
      }
Roel Verdult's avatar
Roel Verdult committed
385 386
    } else {
      // The first block 0x00 is read only, skip this
387
      if (uiBlock == 0 && ! write_block_zero)
388
        continue;
Roel Verdult's avatar
Roel Verdult committed
389

390

Roel Verdult's avatar
Roel Verdult committed
391
      // Make sure a earlier write did not fail
392
      if (!bFailure) {
Roel Verdult's avatar
Roel Verdult committed
393
        // Try to write the data block
394
        memcpy(mp.mpd.abtData, mtDump.amb[uiBlock].mbd.abtData, 16);
395 396
        // do not write a block 0 with incorrect BCC - card will be made invalid!
        if (uiBlock == 0) {
397 398
          if ((mp.mpd.abtData[0] ^ mp.mpd.abtData[1] ^ mp.mpd.abtData[2] ^ mp.mpd.abtData[3] ^ mp.mpd.abtData[4]) != 0x00) {
            printf("!\nError: incorrect BCC in MFD file!\n");
399 400 401
            return false;
          }
        }
402
        if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, uiBlock, &mp))
403
          bFailure = true;
Roel Verdult's avatar
Roel Verdult committed
404 405 406
      }
    }
  }
407 408 409 410
  print_success_or_failure(bFailure, &uiWriteBlocks);
  printf("|\n");
  printf("Done, %d of %d blocks written.\n", uiWriteBlocks, uiBlocks + 1);
  fflush(stdout);
411

Roel Verdult's avatar
Roel Verdult committed
412 413 414
  return true;
}

415
typedef enum {
416 417
  ACTION_READ,
  ACTION_WRITE,
418
  ACTION_USAGE
419 420
} action_t;

421
static void
422
print_usage(const char *pcProgramName)
423
{
424 425 426 427 428 429 430 431 432
  printf("Usage: ");
  printf("%s r|R|w|W a|b <dump.mfd> [<keys.mfd>]\n", pcProgramName);
  printf("  r|R|w|W       - Perform read from (r) or unlocked read from (R) or write to (w) or unlocked write to (W) card\n");
  printf("                  *** note that unlocked write will attempt to overwrite block 0 including UID\n");
  printf("                  *** unlocked read does not require authentication and will reveal A and B keys\n");
  printf("                  *** unlocking only works with special Mifare 1K cards (Chinese clones)\n");
  printf("  a|b           - Use A or B keys for action\n");
  printf("  <dump.mfd>    - MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)\n");
  printf("  <keys.mfd>    - MiFare Dump (MFD) that contain the keys (optional)\n");
433 434
}

435
int
436
main(int argc, const char *argv[])
437
{
438
  action_t atAction = ACTION_USAGE;
439
  uint8_t *pbtUID;
440 441
  FILE   *pfKeys = NULL;
  FILE   *pfDump = NULL;
442
  int    unlock = 0;
443

444
  if (argc < 2) {
445 446
    print_usage(argv[0]);
    exit(EXIT_FAILURE);
Roel Verdult's avatar
Roel Verdult committed
447
  }
448
  const char *command = argv[1];
Roel Verdult's avatar
Roel Verdult committed
449

450
  if (strcmp(command, "r") == 0 || strcmp(command, "R") == 0) {
451
    if (argc < 4) {
452 453
      print_usage(argv[0]);
      exit(EXIT_FAILURE);
454
    }
455
    atAction = ACTION_READ;
456
    if (strcmp(command, "R") == 0)
457
      unlock = 1;
458
    bUseKeyA = tolower((int)((unsigned char) * (argv[2]))) == 'a';
459
    bUseKeyFile = (argc > 4);
460
  } else if (strcmp(command, "w") == 0 || strcmp(command, "W") == 0) {
461
    if (argc < 4) {
462 463
      print_usage(argv[0]);
      exit(EXIT_FAILURE);
464
    }
465
    atAction = ACTION_WRITE;
466
    if (strcmp(command, "W") == 0)
467
      unlock = 1;
468
    bUseKeyA = tolower((int)((unsigned char) * (argv[2]))) == 'a';
469
    bUseKeyFile = (argc > 4);
Roel Verdult's avatar
Roel Verdult committed
470 471
  }

472
  switch (atAction) {
473
    case ACTION_USAGE:
474 475
      print_usage(argv[0]);
      exit(EXIT_FAILURE);
476 477 478 479
      break;
    case ACTION_READ:
    case ACTION_WRITE:
      if (bUseKeyFile) {
480
        pfKeys = fopen(argv[4], "rb");
481
        if (pfKeys == NULL) {
482 483
          printf("Could not open keys file: %s\n", argv[4]);
          exit(EXIT_FAILURE);
484
        }
485 486 487 488
        if (fread(&mtKeys, 1, sizeof(mtKeys), pfKeys) != sizeof(mtKeys)) {
          printf("Could not read keys file: %s\n", argv[4]);
          fclose(pfKeys);
          exit(EXIT_FAILURE);
489
        }
490
        fclose(pfKeys);
491
      }
492

493
      if (atAction == ACTION_READ) {
494
        memset(&mtDump, 0x00, sizeof(mtDump));
495
      } else {
496
        pfDump = fopen(argv[3], "rb");
497

498
        if (pfDump == NULL) {
499 500
          printf("Could not open dump file: %s\n", argv[3]);
          exit(EXIT_FAILURE);
501
        }
502

503 504 505 506
        if (fread(&mtDump, 1, sizeof(mtDump), pfDump) != sizeof(mtDump)) {
          printf("Could not read dump file: %s\n", argv[3]);
          fclose(pfDump);
          exit(EXIT_FAILURE);
507
        }
508
        fclose(pfDump);
509
      }
510
      // printf("Successfully opened required files\n");
511

512
      nfc_init(&context);
513

514
      // Try to open the NFC reader
515
      pnd = nfc_open(context, NULL);
516
      if (pnd == NULL) {
517 518
        printf("Error opening NFC reader\n");
        exit(EXIT_FAILURE);
519
      }
520

521 522 523
      if (nfc_initiator_init(pnd) < 0) {
        nfc_perror(pnd, "nfc_initiator_init");
        exit(EXIT_FAILURE);
524
      };
525

526
      // Let the reader only try once to find a tag
527 528 529
      if (nfc_device_set_property_bool(pnd, NP_INFINITE_SELECT, false) < 0) {
        nfc_perror(pnd, "nfc_device_set_property_bool");
        exit(EXIT_FAILURE);
530 531
      }
      // Disable ISO14443-4 switching in order to read devices that emulate Mifare Classic with ISO14443-4 compliance.
532
      nfc_device_set_property_bool(pnd, NP_AUTO_ISO14443_4, false);
533

534
      printf("NFC reader: %s opened\n", nfc_device_get_name(pnd));
535

536
      // Try to find a MIFARE Classic tag
537
      if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) {
538 539
        printf("Error: no tag was found\n");
        nfc_close(pnd);
540
        nfc_exit(context);
541
        exit(EXIT_FAILURE);
542 543 544
      }
      // Test if we are dealing with a MIFARE compatible tag
      if ((nt.nti.nai.btSak & 0x08) == 0) {
545
        printf("Warning: tag is probably not a MFC!\n");
546
      }
547

548 549
      // Get the info from the current tag
      pbtUID = nt.nti.nai.abtUid;
550

551 552
      if (bUseKeyFile) {
        uint8_t fileUid[4];
553
        memcpy(fileUid, mtKeys.amb[0].mbm.abtUID, 4);
554
        // Compare if key dump UID is the same as the current tag UID, at least for the first 4 bytes
555 556 557
        if (memcmp(pbtUID, fileUid, 4) != 0) {
          printf("Expected MIFARE Classic card with UID starting as: %02x%02x%02x%02x\n",
                 fileUid[0], fileUid[1], fileUid[2], fileUid[3]);
558
        }
559
      }
560
      printf("Found MIFARE Classic card:\n");
561
      print_nfc_target(nt, false);
562 563 564 565 566 567 568 569 570 571 572 573

      // Guessing size
      if ((nt.nti.nai.abtAtqa[1] & 0x02) == 0x02)
        // 4K
        uiBlocks = 0xff;
      else if ((nt.nti.nai.btSak & 0x01) == 0x01)
        // 320b
        uiBlocks = 0x13;
      else
        // 1K
        // TODO: for MFP it is 0x7f (2K) but how to be sure it's a MFP? Try to get RATS?
        uiBlocks = 0x3f;
574
      printf("Guessing size: seems to be a %i-byte card\n", (uiBlocks + 1) * 16);
575 576

      if (atAction == ACTION_READ) {
577 578 579 580
        if (read_card(unlock)) {
          printf("Writing data to file: %s ...", argv[3]);
          fflush(stdout);
          pfDump = fopen(argv[3], "wb");
581
          if (pfDump == NULL) {
582 583
            printf("Could not open dump file: %s\n", argv[3]);
            exit(EXIT_FAILURE);
584
          }
585 586 587
          if (fwrite(&mtDump, 1, sizeof(mtDump), pfDump) != sizeof(mtDump)) {
            printf("\nCould not write to file: %s\n", argv[3]);
            exit(EXIT_FAILURE);
588
          }
589 590
          printf("Done.\n");
          fclose(pfDump);
591
        }
592
      } else if (atAction == ACTION_WRITE) {
593
        write_card(unlock);
594
      }
595

596
      nfc_close(pnd);
597
      break;
598
  };
599

600
  nfc_exit(context);
601
  exit(EXIT_SUCCESS);
Roel Verdult's avatar
Roel Verdult committed
602
}