nfc-read-forum-tag3.c 9.53 KB
Newer Older
1 2
/*-
 * Public platform independent Near Field Communication (NFC) library examples
3
 *
4
 * Copyright (C) 2011 Romuald Conty
5
 *
6 7 8
 * 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,
9
 *  this list of conditions and the following disclaimer.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *  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.
25
 *
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
 * Note that this license only applies on the examples, NFC library itself is under LGPL
 *
 */

/**
 * @file nfc-read-forum-tag3.c
 * @brief Extract NDEF Message from a NFC Forum Tag Type 3
 * This utility extract (if available) the NDEF Message contained in an NFC Forum Tag Type 3.
 */

/*
 * This implementation was written based on information provided by the
 * following documents:
 *
 * NFC Forum Type 3 Tag Operation Specification
 *  Technical Specification
 *  NFCForum-TS-Type-3-Tag_1.1 - 2011-06-28
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif // HAVE_CONFIG_H

#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

#include <nfc/nfc.h>

#include "nfc-utils.h"

58
#if defined(WIN32) && defined(__GNUC__) /* mingw compiler */
59
#include <getopt.h>
60 61
#endif

62
static nfc_device *pnd;
63

Ludovic Rousseau's avatar
Ludovic Rousseau committed
64
static void
65 66
print_usage(char *progname)
{
67 68 69
  fprintf(stderr, "usage: %s -o FILE\n", progname);
  fprintf(stderr, "\nOptions:\n");
  fprintf(stderr, "  -o     Extract NDEF message if available in FILE\n");
70 71
}

72
static void stop_select(int sig)
73 74 75
{
  (void) sig;
  if (pnd)
76
    nfc_abort_command(pnd);
77
  else
78
    exit(EXIT_FAILURE);
79 80
}

Ludovic Rousseau's avatar
Ludovic Rousseau committed
81
static void
82
build_felica_frame(const nfc_felica_info nfi, const uint8_t command, const uint8_t *payload, const size_t payload_len, uint8_t *frame, size_t *frame_len)
83 84 85 86
{
  frame[0] = 1 + 1 + 8 + payload_len;
  *frame_len = frame[0];
  frame[1] = command;
87 88
  memcpy(frame + 2, nfi.abtId, 8);
  memcpy(frame + 10, payload, payload_len);
89 90 91
}

#define CHECK 		0x06
92
static int
93
nfc_forum_tag_type3_check(nfc_device *dev, const nfc_target nt, const uint16_t block, const uint8_t block_count, uint8_t *data, size_t *data_len)
94
{
95
  uint8_t payload[1024] = {
96 97 98 99 100
    1, // Services
    0x0B, 0x00, // NFC Forum Tag Type 3's Service code
    block_count,
    0x80, block, // block 0
  };
101

102
  size_t payload_len = 1 + 2 + 1;
Audrey Diacre's avatar
Audrey Diacre committed
103
  for (uint8_t b = 0; b < block_count; b++) {
104 105 106 107 108 109 110
    if (block < 0x100) {
      payload[payload_len++] = 0x80;
      payload[payload_len++] = block + b;
    } else {
      payload[payload_len++] = 0x00;
      payload[payload_len++] = (block + b) >> 8;
      payload[payload_len++] = (block + b) & 0xff;
111
    }
112 113
  }

114
  uint8_t frame[1024];
115
  size_t frame_len = sizeof(frame);
116
  build_felica_frame(nt.nti.nfi, CHECK, payload, payload_len, frame, &frame_len);
117

118 119
  uint8_t rx[1024];
  int res;
120
  if ((res = nfc_initiator_transceive_bytes(dev, frame, frame_len, rx, sizeof(rx), 0)) < 0) {
121
    return res;
122
  }
123
  const int res_overhead = 1 + 1 + 8 + 2;  // 1+1+8+2: LEN + CMD + NFCID2 + STATUS
124
  if (res < res_overhead) {
125 126 127
    // Not enough data
    return -1;
  }
128 129
  uint8_t felica_res_len = rx[0];
  if (res != felica_res_len) {
130 131 132
    // Error while receiving felica frame
    return -1;
  }
133
  if ((CHECK + 1) != rx[1]) {
134 135 136
    // Command return does not match
    return -1;
  }
137
  if (0 != memcmp(&rx[2], nt.nti.nfi.abtId, 8)) {
138 139 140
    // NFCID2 does not match
    return -1;
  }
141 142
  const uint8_t status_flag1 = rx[10];
  const uint8_t status_flag2 = rx[11];
143 144
  if ((status_flag1) || (status_flag2)) {
    // Felica card's error
145
    fprintf(stderr, "Status bytes: %02x, %02x\n", status_flag1, status_flag2);
146 147 148
    return -1;
  }
  // const uint8_t res_block_count = res[12];
149
  *data_len = res - res_overhead + 1; // +1 => block count is stored on 1 byte
150
  memcpy(data, &rx[res_overhead + 1], *data_len);
151 152 153 154 155 156 157 158 159 160
  return *data_len;
}

int
main(int argc, char *argv[])
{
  (void)argc;
  (void)argv;

  int ch;
161
  char *ndef_output = NULL;
162
  while ((ch = getopt(argc, argv, "ho:")) != -1) {
163
    switch (ch) {
164 165
      case 'h':
        print_usage(argv[0]);
166
        exit(EXIT_SUCCESS);
167 168 169 170 171 172
        break;
      case 'o':
        ndef_output = optarg;
        break;
      case '?':
        if (optopt == 'o')
173
          fprintf(stderr, "Option -%c requires an argument.\n", optopt);
174
      default:
175 176
        print_usage(argv[0]);
        exit(EXIT_FAILURE);
177 178 179 180
    }
  }

  if (ndef_output == NULL) {
181 182
    print_usage(argv[0]);
    exit(EXIT_FAILURE);
183
  }
184 185
  FILE *message_stream = NULL;
  FILE *ndef_stream = NULL;
186

187
  if ((strlen(ndef_output) == 1) && (ndef_output[0] == '-')) {
188 189 190 191 192 193
    message_stream = stderr;
    ndef_stream = stdout;
  } else {
    message_stream = stdout;
    ndef_stream = fopen(ndef_output, "wb");
    if (!ndef_stream) {
194 195
      fprintf(stderr, "Could not open file %s.\n", ndef_output);
      exit(EXIT_FAILURE);
196 197 198
    }
  }

199 200
  nfc_context *context;
  nfc_init(&context);
201

202
  pnd = nfc_open(context, NULL);
203 204

  if (pnd == NULL) {
205
    ERR("Unable to open NFC device");
206
    exit(EXIT_FAILURE);
207 208
  }

209
  fprintf(message_stream, "NFC device: %s opened\n", nfc_device_get_name(pnd));
210

211
  nfc_modulation nm = {
212 213 214 215
    .nmt = NMT_FELICA,
    .nbr = NBR_212,
  };

216
  signal(SIGINT, stop_select);
217

218
  nfc_target nt;
219

220 221 222
  if (nfc_initiator_init(pnd) < 0) {
    nfc_perror(pnd, "nfc_initiator_init");
    exit(EXIT_FAILURE);
223
  }
224
  fprintf(message_stream, "Place your NFC Forum Tag Type 3 in the field...\n");
225 226 227

  int error = EXIT_SUCCESS;
  // Polling payload (SENSF_REQ) must be present (see NFC Digital Protol)
228
  const uint8_t *pbtSensfReq = (uint8_t *)"\x00\xff\xff\x01\x00";
229
  if (nfc_initiator_select_passive_target(pnd, nm, pbtSensfReq, 5, &nt) <= 0) {
230
    nfc_perror(pnd, "nfc_initiator_select_passive_target");
231 232 233 234
    error = EXIT_FAILURE;
    goto error;
  }

235
  // Check if System Code equals 0x12fc
236
  const uint8_t abtNfcForumSysCode[] = { 0x12, 0xfc };
237
  if (0 != memcmp(nt.nti.nfi.abtSysCode, abtNfcForumSysCode, 2)) {
238
    // Retry with special polling
239
    const uint8_t *pbtSensfReqNfcForum = (uint8_t *)"\x00\x12\xfc\x01\x00";
240
    if (nfc_initiator_select_passive_target(pnd, nm, pbtSensfReqNfcForum, 5, &nt) <= 0) {
241
      nfc_perror(pnd, "nfc_initiator_select_passive_target");
242 243 244
      error = EXIT_FAILURE;
      goto error;
    }
245
    // Check again if System Code equals 0x12fc
246 247
    if (0 != memcmp(nt.nti.nfi.abtSysCode, abtNfcForumSysCode, 2)) {
      fprintf(stderr, "Tag is not NFC Forum Tag Type 3 compliant.\n");
248 249 250 251 252 253 254
      error = EXIT_FAILURE;
      goto error;
    }
  }

  //print_nfc_felica_info(nt.nti.nfi, true);

255 256
  if ((nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false) < 0) || (nfc_device_set_property_bool(pnd, NP_INFINITE_SELECT, false) < 0)) {
    nfc_perror(pnd, "nfc_device_set_property_bool");
257 258 259 260
    error = EXIT_FAILURE;
    goto error;
  }

261
  uint8_t data[1024];
262 263 264
  size_t data_len = sizeof(data);
  int len;

265 266
  if (0 >= (len = nfc_forum_tag_type3_check(pnd, nt, 0, 1, data, &data_len))) {
    nfc_perror(pnd, "nfc_forum_tag_type3_check");
267 268 269 270 271 272
    error = EXIT_FAILURE;
    goto error;
  }

  const int ndef_major_version = (data[0] & 0xf0) >> 4;
  const int ndef_minor_version = (data[0] & 0x0f);
273
  fprintf(message_stream, "NDEF Mapping version: %d.%d\n", ndef_major_version, ndef_minor_version);
274 275

  const int available_block_count = (data[3] << 8) + data[4];
276
  fprintf(message_stream, "NFC Forum Tag Type 3 capacity: %d bytes\n", available_block_count * 16);
277 278

  uint32_t ndef_data_len = (data[11] << 16) + (data[12] << 8) + data[13];
279
  fprintf(message_stream, "NDEF data length: %d bytes\n", ndef_data_len);
280 281

  uint16_t ndef_calculated_checksum = 0;
Audrey Diacre's avatar
Audrey Diacre committed
282
  for (size_t n = 0; n < 14; n++)
283 284 285 286
    ndef_calculated_checksum += data[n];

  const uint16_t ndef_checksum = (data[14] << 8) + data[15];
  if (ndef_calculated_checksum != ndef_checksum) {
287
    fprintf(stderr, "NDEF CRC does not match with calculated one\n");
288 289 290 291 292
    error = EXIT_FAILURE;
    goto error;
  }

  if (!ndef_data_len) {
293
    fprintf(stderr, "Empty NFC Forum Tag Type 3\n");
294 295 296 297 298 299 300 301
    error = EXIT_FAILURE;
    goto error;
  }

  const uint8_t block_max_per_check = data[1];
  const uint16_t block_count_to_check = (ndef_data_len / 16) + 1;

  data_len = 0;
302
  for (uint16_t b = 0; b < (block_count_to_check / block_max_per_check); b += block_max_per_check) {
303
    size_t size = sizeof(data) - data_len;
304 305
    if (!nfc_forum_tag_type3_check(pnd, nt, 1 + b, MIN(block_max_per_check, (block_count_to_check - (b * block_max_per_check))), data + data_len, &size)) {
      nfc_perror(pnd, "nfc_forum_tag_type3_check");
306 307 308
      error = EXIT_FAILURE;
      goto error;
    }
309
    data_len += size;
310
  }
311 312
  if (fwrite(data, 1, data_len, ndef_stream) != data_len) {
    fprintf(stderr, "Could not write to file.\n");
313 314 315 316 317
    error = EXIT_FAILURE;
    goto error;
  }

error:
318
  fclose(ndef_stream);
319
  if (pnd) {
320
    nfc_close(pnd);
321
  }
322
  nfc_exit(context);
323
  exit(error);
324
}