libnfc  1.7.0-rc7
nfc-read-forum-tag3.c
Go to the documentation of this file.
1 /*-
2  * Free/Libre Near Field Communication (NFC) library
3  *
4  * Libnfc historical contributors:
5  * Copyright (C) 2009 Roel Verdult
6  * Copyright (C) 2009-2013 Romuald Conty
7  * Copyright (C) 2010-2012 Romain Tartière
8  * Copyright (C) 2010-2013 Philippe Teuwen
9  * Copyright (C) 2012-2013 Ludovic Rousseau
10  * Additional contributors of this file:
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  * 1) Redistributions of source code must retain the above copyright notice,
15  * this list of conditions and the following disclaimer.
16  * 2 )Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in the
18  * documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  * Note that this license only applies on the examples, NFC library itself is under LGPL
33  *
34  */
35 
42 /*
43  * This implementation was written based on information provided by the
44  * following documents:
45  *
46  * NFC Forum Type 3 Tag Operation Specification
47  * Technical Specification
48  * NFCForum-TS-Type-3-Tag_1.1 - 2011-06-28
49  */
50 
51 #ifdef HAVE_CONFIG_H
52 # include "config.h"
53 #endif // HAVE_CONFIG_H
54 
55 #include <errno.h>
56 #include <signal.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 
60 #include <nfc/nfc.h>
61 
62 #include "nfc-utils.h"
63 
64 #if defined(WIN32) && defined(__GNUC__) /* mingw compiler */
65 #include <getopt.h>
66 #endif
67 
68 static nfc_device *pnd;
69 static nfc_context *context;
70 
71 static void
72 print_usage(char *progname)
73 {
74  fprintf(stderr, "usage: %s -o FILE\n", progname);
75  fprintf(stderr, "\nOptions:\n");
76  fprintf(stderr, " -o Extract NDEF message if available in FILE\n");
77 }
78 
79 static void stop_select(int sig)
80 {
81  (void) sig;
82  if (pnd != NULL) {
83  nfc_abort_command(pnd);
84  } else {
85  nfc_exit(context);
86  exit(EXIT_FAILURE);
87  }
88 }
89 
90 static void
91 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)
92 {
93  frame[0] = 1 + 1 + 8 + payload_len;
94  *frame_len = frame[0];
95  frame[1] = command;
96  memcpy(frame + 2, nfi.abtId, 8);
97  memcpy(frame + 10, payload, payload_len);
98 }
99 
100 #define CHECK 0x06
101 static int
102 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)
103 {
104  uint8_t payload[1024] = {
105  1, // Services
106  0x0B, 0x00, // NFC Forum Tag Type 3's Service code
107  block_count,
108  0x80, block, // block 0
109  };
110 
111  size_t payload_len = 1 + 2 + 1;
112  for (uint8_t b = 0; b < block_count; b++) {
113  if (block < 0x100) {
114  payload[payload_len++] = 0x80;
115  payload[payload_len++] = block + b;
116  } else {
117  payload[payload_len++] = 0x00;
118  payload[payload_len++] = (block + b) >> 8;
119  payload[payload_len++] = (block + b) & 0xff;
120  }
121  }
122 
123  uint8_t frame[1024];
124  size_t frame_len = sizeof(frame);
125  build_felica_frame(nt.nti.nfi, CHECK, payload, payload_len, frame, &frame_len);
126 
127  uint8_t rx[1024];
128  int res;
129  if ((res = nfc_initiator_transceive_bytes(dev, frame, frame_len, rx, sizeof(rx), 0)) < 0) {
130  return res;
131  }
132  const int res_overhead = 1 + 1 + 8 + 2; // 1+1+8+2: LEN + CMD + NFCID2 + STATUS
133  if (res < res_overhead) {
134  // Not enough data
135  return -1;
136  }
137  uint8_t felica_res_len = rx[0];
138  if (res != felica_res_len) {
139  // Error while receiving felica frame
140  return -1;
141  }
142  if ((CHECK + 1) != rx[1]) {
143  // Command return does not match
144  return -1;
145  }
146  if (0 != memcmp(&rx[2], nt.nti.nfi.abtId, 8)) {
147  // NFCID2 does not match
148  return -1;
149  }
150  const uint8_t status_flag1 = rx[10];
151  const uint8_t status_flag2 = rx[11];
152  if ((status_flag1) || (status_flag2)) {
153  // Felica card's error
154  fprintf(stderr, "Status bytes: %02x, %02x\n", status_flag1, status_flag2);
155  return -1;
156  }
157  // const uint8_t res_block_count = res[12];
158  *data_len = res - res_overhead + 1; // +1 => block count is stored on 1 byte
159  memcpy(data, &rx[res_overhead + 1], *data_len);
160  return *data_len;
161 }
162 
163 int
164 main(int argc, char *argv[])
165 {
166  (void)argc;
167  (void)argv;
168 
169  int ch;
170  char *ndef_output = NULL;
171  while ((ch = getopt(argc, argv, "ho:")) != -1) {
172  switch (ch) {
173  case 'h':
174  print_usage(argv[0]);
175  exit(EXIT_SUCCESS);
176  break;
177  case 'o':
178  ndef_output = optarg;
179  break;
180  case '?':
181  if (optopt == 'o')
182  fprintf(stderr, "Option -%c requires an argument.\n", optopt);
183  default:
184  print_usage(argv[0]);
185  exit(EXIT_FAILURE);
186  }
187  }
188 
189  if (ndef_output == NULL) {
190  print_usage(argv[0]);
191  exit(EXIT_FAILURE);
192  }
193  FILE *message_stream = NULL;
194  FILE *ndef_stream = NULL;
195 
196  if ((strlen(ndef_output) == 1) && (ndef_output[0] == '-')) {
197  message_stream = stderr;
198  ndef_stream = stdout;
199  } else {
200  message_stream = stdout;
201  ndef_stream = fopen(ndef_output, "wb");
202  if (!ndef_stream) {
203  fprintf(stderr, "Could not open file %s.\n", ndef_output);
204  exit(EXIT_FAILURE);
205  }
206  }
207 
208  nfc_init(&context);
209  if (context == NULL) {
210  ERR("Unable to init libnfc (malloc)\n");
211  exit(EXIT_FAILURE);
212  }
213 
214  pnd = nfc_open(context, NULL);
215 
216  if (pnd == NULL) {
217  ERR("Unable to open NFC device");
218  fclose(ndef_stream);
219  nfc_exit(context);
220  exit(EXIT_FAILURE);
221  }
222 
223  fprintf(message_stream, "NFC device: %s opened\n", nfc_device_get_name(pnd));
224 
225  nfc_modulation nm = {
226  .nmt = NMT_FELICA,
227  .nbr = NBR_212,
228  };
229 
230  signal(SIGINT, stop_select);
231 
232  nfc_target nt;
233 
234  if (nfc_initiator_init(pnd) < 0) {
235  nfc_perror(pnd, "nfc_initiator_init");
236  fclose(ndef_stream);
237  nfc_close(pnd);
238  nfc_exit(context);
239  exit(EXIT_FAILURE);
240  }
241  fprintf(message_stream, "Place your NFC Forum Tag Type 3 in the field...\n");
242 
243  // Polling payload (SENSF_REQ) must be present (see NFC Digital Protol)
244  const uint8_t *pbtSensfReq = (uint8_t *)"\x00\xff\xff\x01\x00";
245  if (nfc_initiator_select_passive_target(pnd, nm, pbtSensfReq, 5, &nt) <= 0) {
246  nfc_perror(pnd, "nfc_initiator_select_passive_target");
247  fclose(ndef_stream);
248  nfc_close(pnd);
249  nfc_exit(context);
250  exit(EXIT_FAILURE);
251  }
252 
253  // Check if System Code equals 0x12fc
254  const uint8_t abtNfcForumSysCode[] = { 0x12, 0xfc };
255  if (0 != memcmp(nt.nti.nfi.abtSysCode, abtNfcForumSysCode, 2)) {
256  // Retry with special polling
257  const uint8_t *pbtSensfReqNfcForum = (uint8_t *)"\x00\x12\xfc\x01\x00";
258  if (nfc_initiator_select_passive_target(pnd, nm, pbtSensfReqNfcForum, 5, &nt) <= 0) {
259  nfc_perror(pnd, "nfc_initiator_select_passive_target");
260  fclose(ndef_stream);
261  nfc_close(pnd);
262  nfc_exit(context);
263  exit(EXIT_FAILURE);
264  }
265  // Check again if System Code equals 0x12fc
266  if (0 != memcmp(nt.nti.nfi.abtSysCode, abtNfcForumSysCode, 2)) {
267  fprintf(stderr, "Tag is not NFC Forum Tag Type 3 compliant.\n");
268  fclose(ndef_stream);
269  nfc_close(pnd);
270  nfc_exit(context);
271  exit(EXIT_FAILURE);
272  }
273  }
274 
275  //print_nfc_felica_info(nt.nti.nfi, true);
276 
278  nfc_perror(pnd, "nfc_device_set_property_bool");
279  fclose(ndef_stream);
280  nfc_close(pnd);
281  nfc_exit(context);
282  exit(EXIT_FAILURE);
283  }
284 
285  uint8_t data[1024];
286  size_t data_len = sizeof(data);
287 
288  if (nfc_forum_tag_type3_check(pnd, nt, 0, 1, data, &data_len) <= 0) {
289  nfc_perror(pnd, "nfc_forum_tag_type3_check");
290  fclose(ndef_stream);
291  nfc_close(pnd);
292  nfc_exit(context);
293  exit(EXIT_FAILURE);
294  }
295 
296  const int ndef_major_version = (data[0] & 0xf0) >> 4;
297  const int ndef_minor_version = (data[0] & 0x0f);
298  fprintf(message_stream, "NDEF Mapping version: %d.%d\n", ndef_major_version, ndef_minor_version);
299 
300  const int available_block_count = (data[3] << 8) + data[4];
301  fprintf(message_stream, "NFC Forum Tag Type 3 capacity: %d bytes\n", available_block_count * 16);
302 
303  uint32_t ndef_data_len = (data[11] << 16) + (data[12] << 8) + data[13];
304  fprintf(message_stream, "NDEF data length: %d bytes\n", ndef_data_len);
305 
306  uint16_t ndef_calculated_checksum = 0;
307  for (size_t n = 0; n < 14; n++)
308  ndef_calculated_checksum += data[n];
309 
310  const uint16_t ndef_checksum = (data[14] << 8) + data[15];
311  if (ndef_calculated_checksum != ndef_checksum) {
312  fprintf(stderr, "NDEF CRC does not match with calculated one\n");
313  fclose(ndef_stream);
314  nfc_close(pnd);
315  nfc_exit(context);
316  exit(EXIT_FAILURE);
317  }
318 
319  if (!ndef_data_len) {
320  fprintf(stderr, "Empty NFC Forum Tag Type 3\n");
321  fclose(ndef_stream);
322  nfc_close(pnd);
323  nfc_exit(context);
324  exit(EXIT_FAILURE);
325  }
326 
327  const uint8_t block_max_per_check = data[1];
328  const uint16_t block_count_to_check = (ndef_data_len / 16) + 1;
329 
330  data_len = 0;
331  for (uint16_t b = 0; b < (block_count_to_check / block_max_per_check); b += block_max_per_check) {
332  size_t size = sizeof(data) - data_len;
333  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)) {
334  nfc_perror(pnd, "nfc_forum_tag_type3_check");
335  fclose(ndef_stream);
336  nfc_close(pnd);
337  nfc_exit(context);
338  exit(EXIT_FAILURE);
339  }
340  data_len += size;
341  }
342  if (fwrite(data, 1, data_len, ndef_stream) != data_len) {
343  fprintf(stderr, "Could not write to file.\n");
344  fclose(ndef_stream);
345  nfc_close(pnd);
346  nfc_exit(context);
347  exit(EXIT_FAILURE);
348  }
349 
350  fclose(ndef_stream);
351  nfc_close(pnd);
352  nfc_exit(context);
353  exit(EXIT_SUCCESS);
354 }