From 6049acaf5a73c421f32470dd79d112060aec7375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Tarti=C3=A8re?= Date: Wed, 13 May 2015 02:06:55 +0200 Subject: [PATCH] Introduce experimental FeliCa Lite API. --- .gitignore | 2 + README.md | 31 ++--- examples/Makefile.am | 10 +- examples/felica-lite-dump.c | 104 ++++++++++++++++ examples/felica-read-ndef.c | 164 +++++++++++++++++++++++++ libfreefare/Makefile.am | 3 +- libfreefare/felica.c | 211 ++++++++++++++++++++++++++++++++ libfreefare/freefare.3 | 1 + libfreefare/freefare.c | 57 ++++++++- libfreefare/freefare.h | 12 ++ libfreefare/freefare_internal.h | 8 ++ test/Makefile.am | 6 + test/felica_fixture.c | 82 +++++++++++++ test/felica_fixture.h | 18 +++ test/test_felica.c | 38 ++++++ 15 files changed, 726 insertions(+), 21 deletions(-) create mode 100644 examples/felica-lite-dump.c create mode 100644 examples/felica-read-ndef.c create mode 100644 libfreefare/felica.c create mode 100644 test/felica_fixture.c create mode 100644 test/felica_fixture.h create mode 100644 test/test_felica.c diff --git a/.gitignore b/.gitignore index 5d4bb95..9394e33 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,8 @@ ltmain.sh missing stamp-h1 test-driver +examples/felica-lite-dump +examples/felica-read-ndef examples/mifare-classic-format examples/mifare-classic-read-ndef examples/mifare-classic-write-ndef diff --git a/README.md b/README.md index 06e55c4..2898b4e 100644 --- a/README.md +++ b/README.md @@ -8,21 +8,22 @@ If you are new to _libfreefare_ or the _nfc-tools_, you should collect useful in # Feature matrix ## Tags -| Tag | Status | -|:-------------------|:--------------| -| MIFARE Classic 1k | Supported | -| MIFARE Classic 4k | Supported | -| MIFARE DESFire 2k | Supported | -| MIFARE DESFire 4k | Supported | -| MIFARE DESFire 8k | Supported | -| MIFARE DESFire EV1 | Supported | -| MIFARE Mini | Not supported | -| MIFARE Plus S 2k | Not supported | -| MIFARE Plus S 4k | Not supported | -| MIFARE Plus X 2k | Not supported | -| MIFARE Plus X 4k | Not supported | -| MIFARE Ultralight | Supported | -| MIFARE UltralightC | Supported | +| Tag | Status | +|:--------------------|:--------------| +| FeliCa Lite | Supported | +| MIFARE Classic 1k | Supported | +| MIFARE Classic 4k | Supported | +| MIFARE DESFire 2k | Supported | +| MIFARE DESFire 4k | Supported | +| MIFARE DESFire 8k | Supported | +| MIFARE DESFire EV1 | Supported | +| MIFARE Mini | Not supported | +| MIFARE Plus S 2k | Not supported | +| MIFARE Plus S 4k | Not supported | +| MIFARE Plus X 2k | Not supported | +| MIFARE Plus X 4k | Not supported | +| MIFARE Ultralight | Supported | +| MIFARE Ultralight C | Supported | ## Specifications | Specification | Status | diff --git a/examples/Makefile.am b/examples/Makefile.am index 1d63de9..eece1dd 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,7 +1,9 @@ AM_CFLAGS = -I. -I$(top_srcdir)/libfreefare @LIBNFC_CFLAGS@ AM_LDFLAGS = @LIBNFC_LIBS@ -bin_PROGRAMS = mifare-classic-format \ +bin_PROGRAMS = felica-lite-dump \ + felica-read-ndef \ + mifare-classic-format \ mifare-classic-write-ndef \ mifare-classic-read-ndef \ mifare-desfire-access \ @@ -15,6 +17,12 @@ bin_PROGRAMS = mifare-classic-format \ mifare-desfire-write-ndef \ mifare-ultralight-info +felica_lite_dump_SOURCES = felica-lite-dump.c +felica_lite_dump_LDADD = $(top_builddir)/libfreefare/libfreefare.la + +felica_read_ndef_SOURCES = felica-read-ndef.c +felica_read_ndef_LDADD = $(top_builddir)/libfreefare/libfreefare.la + mifare_classic_format_SOURCES = mifare-classic-format.c mifare_classic_format_LDADD = $(top_builddir)/libfreefare/libfreefare.la diff --git a/examples/felica-lite-dump.c b/examples/felica-lite-dump.c new file mode 100644 index 0000000..df77be9 --- /dev/null +++ b/examples/felica-lite-dump.c @@ -0,0 +1,104 @@ +/*- + * Copyright (C) 2015, Romain Tartiere. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +#include +#include + +#include + +#include + +int +main (void) +{ + nfc_device *device = NULL; + FreefareTag *tags = NULL; + nfc_connstring devices[8]; + + nfc_context *context; + nfc_init (&context); + if (context == NULL) + errx (EXIT_FAILURE, "Unable to init libnfc (malloc)"); + + size_t device_count = nfc_list_devices (context, devices, 8); + if (device_count <= 0) + errx (EXIT_FAILURE, "No NFC device found."); + + for (size_t d = 0; d < device_count; d++) { + device = nfc_open (context, devices[d]); + if (!device) { + errx (EXIT_FAILURE, "nfc_open() failed."); + } + + tags = freefare_get_tags (device); + if (!tags) { + nfc_close (device); + errx (EXIT_FAILURE, "Error listing FeliCa tag."); + } + + for (int i = 0; tags[i]; i++) { + int r = felica_connect (tags[i]); + if (r < 0) + errx (EXIT_FAILURE, "Cannot connect to FeliCa target"); + + printf ("Dumping %s tag %s\n", freefare_get_tag_friendly_name (tags[i]), freefare_get_tag_uid (tags[i])); + printf ("Number\tName\tData\n"); + + for (int block = 0x00; block < 0x0f; block++) { + uint8_t buffer[16]; + + if (felica_read (tags[i], FELICA_SC_RO, block, buffer, sizeof (buffer)) < 0) + errx (EXIT_FAILURE, "Error reading block %d", block); + + if (block < 0x0e) + printf ("0x%02x\tS_PAD%d\t", block, block); + else + printf ("0x%02x\tREG\t", block); + for (int j = 0; j < 16; j++) { + printf ("%02x ", buffer[j]); + } + printf ("\n"); + } + + char *block_names[] = { + "RC", "MAC", "ID", "D_ID", "SER_C", "SYS_C", "CKV", "CK", "MC", + }; + int valid_bytes[] = { + 16, 8, 16, 16, 2, 2, 2, 16, 5 + }; + for (int block = 0x80; block < 0x89; block++) { + uint8_t buffer[16]; + + if (felica_read (tags[i], FELICA_SC_RO, block, buffer, sizeof (buffer)) < 0) + errx (EXIT_FAILURE, "Error reading block %d", block); + + printf ("0x%02x\t%s\t", block, block_names[block - 0x80]); + for (int j = 0; j < valid_bytes[block - 0x80]; j++) { + printf ("%02x ", buffer[j]); + } + printf ("\n"); + } + + felica_disconnect (tags[i]); + } + + freefare_free_tags (tags); + nfc_close (device); + } + + exit(EXIT_SUCCESS); +} diff --git a/examples/felica-read-ndef.c b/examples/felica-read-ndef.c new file mode 100644 index 0000000..0c5bc77 --- /dev/null +++ b/examples/felica-read-ndef.c @@ -0,0 +1,164 @@ +/*- + * Copyright (C) 2015, Romain Tartiere. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +#include "config.h" + +#include +#include +#include +#include + +#if defined(HAVE_SYS_ENDIAN_H) +# include +#endif + +#if defined(HAVE_ENDIAN_H) +# include +#endif + +#include + +#include + +#define NDEF_BUFFER_SIZE 512 + +void +usage (char *progname) +{ + fprintf (stderr, "usage: %s [options]\n", progname); + fprintf (stderr, "\nAvailable options:\n"); + fprintf (stderr, " -o FILE Write NDEF message to FILE\n"); +} + +int +main (int argc, char *argv[]) +{ + int error = EXIT_SUCCESS; + nfc_device *device = NULL; + FreefareTag *tags = NULL; + + int ch; + char *ndef_file = NULL; + while ((ch = getopt (argc, argv, "o:")) != -1) { + switch (ch) { + case 'o': + ndef_file = optarg; + case '?': + usage (argv[0]); + exit (EXIT_FAILURE); + } + } + + nfc_connstring devices[8]; + + size_t device_count; + + nfc_context *context; + nfc_init (&context); + if (context == NULL) + errx (EXIT_FAILURE, "Unable to init libnfc (malloc)"); + + device_count = nfc_list_devices (context, devices, 8); + if (device_count <= 0) + errx (EXIT_FAILURE, "No NFC device found."); + + for (size_t d = 0; d < device_count; d++) { + device = nfc_open (context, devices[d]); + if (!device) { + warnx ("nfc_open() failed."); + error = EXIT_FAILURE; + continue; + } + + tags = freefare_get_tags (device); + if (!tags) { + nfc_close (device); + errx (EXIT_FAILURE, "Error listing FeliCa tag."); + } + + for (int i = 0; (!error) && tags[i]; i++) { + int r = felica_connect (tags[i]); + if (r < 0) + errx (EXIT_FAILURE, "Cannot connect to FeliCa target"); + + int block = 1; + uint8_t ndef_message[NDEF_BUFFER_SIZE]; + int ndef_space_left = NDEF_BUFFER_SIZE; + uint8_t *p = ndef_message; + ssize_t s; + + // FIXME Instead of reading as much as we can, we should read until end of NDEF record (if any is found). + while ((ndef_space_left >= 16) && (s = felica_read (tags[i], FELICA_SC_RO, block++, p, 16)) > 0) { + p += s; + ndef_space_left -= s; + } + + size_t ndef_message_length = 0; + + if ((ndef_message[0] & 0x80) == 0x80) { + uint8_t *ndef_record; + do { + ndef_record = ndef_message + ndef_message_length; + + size_t ndef_record_length = 1 + 1 + ndef_record[1]; + size_t payload_length; + size_t payload_length_length; + + if (ndef_record[0] & 0x10) { + /* Short record */ + payload_length = ndef_record[2]; + payload_length_length = 1; + } else { + payload_length = be32toh ((uint32_t) ndef_record + 2); + payload_length_length = 4; + } + ndef_record_length += payload_length_length; + ndef_record_length += payload_length; + + if (ndef_record[0] & 0x08) { + ndef_record_length += 2; + } + + ndef_message_length += ndef_record_length; + if (ndef_message_length > NDEF_BUFFER_SIZE) + errx (EXIT_FAILURE, "NDEF message truncated"); + + } while ((ndef_record[0] & 0x40) != 0x40); + } + + if (ndef_message_length == 0) + errx (EXIT_FAILURE, "No NDEF message found"); + + FILE *f; + if (ndef_file) + f = fopen (ndef_file, "w"); + else + f = stdout; + + if (fwrite (ndef_message, ndef_message_length, 1, f) != 1) + err (EXIT_FAILURE, "Can't write NDEF message"); + + fclose (f); + + felica_disconnect (tags[i]); + } + + freefare_free_tags (tags); + nfc_close (device); + } + exit(EXIT_SUCCESS); +} diff --git a/libfreefare/Makefile.am b/libfreefare/Makefile.am index 326e4c4..da2d652 100644 --- a/libfreefare/Makefile.am +++ b/libfreefare/Makefile.am @@ -3,7 +3,8 @@ AM_LDFLAGS = @LIBNFC_LIBS@ lib_LTLIBRARIES = libfreefare.la -libfreefare_la_SOURCES = freefare.c \ +libfreefare_la_SOURCES = felica.c \ + freefare.c \ mifare_classic.c \ mifare_ultralight.c \ mifare_desfire.c \ diff --git a/libfreefare/felica.c b/libfreefare/felica.c new file mode 100644 index 0000000..ef06130 --- /dev/null +++ b/libfreefare/felica.c @@ -0,0 +1,211 @@ +/*- + * Copyright (C) 2015, Romain Tartiere. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +/* + * This implementation was written based on information provided by the + * following documents: + * + * FeliCa Lite User's Manual + * Version 1.2 + * No. M624-E01-20 + */ + +#include "config.h" + +#include +#include +#include + +#include + +#ifdef WITH_DEBUG +# include +#endif + +#include +#include "freefare_internal.h" + +#define MAX_BLOCK_COUNT 8 + +inline static +ssize_t felica_transceive (FreefareTag tag, uint8_t *data_in, uint8_t *data_out, size_t data_out_length) +{ + DEBUG_XFER (data_in, data_in_length, "===> "); + ssize_t res = nfc_initiator_transceive_bytes (tag->device, data_in, data_in[0], data_out, data_out_length, 0); + DEBUG_XFER (data_out, res, "<=== "); + return res; +} + +FreefareTag +felica_tag_new (void) +{ + return malloc (sizeof (struct felica_tag)); +} + +void +felica_tag_free (FreefareTag tag) +{ + free (tag); +} + + + +int +felica_connect (FreefareTag tag) +{ + ASSERT_INACTIVE (tag); + ASSERT_FELICA (tag); + + nfc_target pnti; + nfc_modulation modulation = { + .nmt = NMT_FELICA, + .nbr = NBR_212, + }; + if (nfc_initiator_select_passive_target (tag->device, modulation, tag->info.nti.nfi.abtId, 0, &pnti) >= 0) { + tag->active = 1; + } else { + errno = EIO; + return -1; + } + return 0; +} + +int +felica_disconnect (FreefareTag tag) +{ + ASSERT_ACTIVE (tag); + ASSERT_FELICA (tag); + + if (nfc_initiator_deselect_target (tag->device) >= 0) { + tag->active = 0; + } else { + errno = EIO; + return -1; + } + return 0; +} + + + +ssize_t +felica_read_ex (FreefareTag tag, uint16_t service, uint8_t block_count, uint8_t blocks[], uint8_t *data, size_t length) +{ + ASSERT_ACTIVE (tag); + ASSERT_FELICA (tag); + + assert (block_count <= MAX_BLOCK_COUNT); + assert (length == 16 * block_count); + + DEBUG_FUNCTION(); + + uint8_t cmd[1 + 1 + 8 + 1 + 2 + 1 + 2 * MAX_BLOCK_COUNT] = { + 0x00, /* Length */ + 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, + 0x00, 0x00, /* Service */ + 0x00, + /* Block ... */ + }; + + uint8_t res[100]; + + cmd[0] = 14 + 2 * block_count; + memcpy (cmd + 2, tag->info.nti.nfi.abtId, 8); + cmd[11] = service; + cmd[12] = service >> 8; + cmd[13] = block_count; + + for (int i = 0; i < block_count; i++) { + cmd[14 + 2*i] = 0x80; + cmd[14 + 2*i + 1] = blocks[i]; + } + + int cnt = felica_transceive (tag, cmd, res, sizeof (res)); + if (cnt != 1 + 1 + 8 + 1 + 1 + 1 + 16 * block_count) { + return -1; + } + size_t len = MIN(res[12] * 16, length); + memcpy (data, res + 13, len); + + return len; +} + +ssize_t +felica_read (FreefareTag tag, uint16_t service, uint8_t block, uint8_t *data, size_t length) +{ + uint8_t blocks[] = { + block + }; + + return felica_read_ex (tag, service, 1, blocks, data, length); +} + +ssize_t +felica_write_ex (FreefareTag tag, uint16_t service, uint8_t block_count, uint8_t blocks[], uint8_t *data, size_t length) +{ + ASSERT_ACTIVE (tag); + ASSERT_FELICA (tag); + + DEBUG_FUNCTION(); + + assert (block_count <= MAX_BLOCK_COUNT); + assert (length == 16 * block_count); + + uint8_t cmd[1 + 1 + 8 + 1 + 2 + 1 + 2 + 16 * MAX_BLOCK_COUNT] = { + 0x00, /* Length */ + 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, + 0x00, 0x00, /* Service */ + 0x00, + /* Block ... */ + /* n * 16 */ + }; + + uint8_t res[12]; + + cmd[0] = 1 + 1 + 8 + 1 + 2 * 1 + 1 + 2 * 1 + 16 * block_count; + memcpy (cmd + 2, tag->info.nti.nfi.abtId, 8); + cmd[11] = service; + cmd[12] = service >> 8; + cmd[13] = block_count; + + for (int i = 0; i < block_count; i++) { + cmd[14 + 2*i] = 0x80; + cmd[14 + 2*i + 1] = blocks[i]; + } + + memcpy (cmd + 14 + 2 * block_count, data, length); + + ssize_t cnt = felica_transceive (tag, cmd, res, sizeof (res)); + + if (cnt != sizeof (res)) + return -1; + + return res[10] == 0 ? 0 : -1; +} + +ssize_t +felica_write (FreefareTag tag, uint16_t service, uint8_t block, uint8_t *data, size_t length) +{ + uint8_t blocks[] = { + block + }; + + return felica_write_ex (tag, service, 1, blocks, data, length); +} diff --git a/libfreefare/freefare.3 b/libfreefare/freefare.3 index bea39a9..a504a81 100644 --- a/libfreefare/freefare.3 +++ b/libfreefare/freefare.3 @@ -50,6 +50,7 @@ Mifare card manipulation library (libfreefare, \-lfreefare) .Fn freefare_get_tags "nfc_device_t *device" .Bd -literal enum freefare_tag_type { + FELICA, MIFARE_CLASSIC_1K, MIFARE_CLASSIC_4K, MIFARE_DESFIRE, diff --git a/libfreefare/freefare.c b/libfreefare/freefare.c index ed947f9..320440f 100644 --- a/libfreefare/freefare.c +++ b/libfreefare/freefare.c @@ -27,6 +27,7 @@ #define NXP_MANUFACTURER_CODE 0x04 struct supported_tag supported_tags[] = { + { FELICA, "FeliCA", NMT_FELICA, 0x00, 0, 0, { 0x00 }, NULL }, { MIFARE_CLASSIC_1K, "Mifare Classic 1k", NMT_ISO14443A, 0x08, 0, 0, { 0x00 }, NULL }, { MIFARE_CLASSIC_1K, "Mifare Classic 1k (Emulated)", NMT_ISO14443A, 0x28, 0, 0, { 0x00 }, NULL }, { MIFARE_CLASSIC_1K, "Mifare Classic 1k (Emulated)", NMT_ISO14443A, 0x68, 0, 0, { 0x00 }, NULL }, @@ -55,6 +56,11 @@ freefare_tag_new (nfc_device *device, nfc_target target) if (target.nm.nmt != supported_tags[i].modulation_type) continue; + if (target.nm.nmt == NMT_FELICA) { + tag_info = &(supported_tags[i]); + found = true; + break; + } if ((target.nm.nmt == NMT_ISO14443A) && ((target.nti.nai.szUidLen == 4) || (target.nti.nai.abtUid[0] == NXP_MANUFACTURER_CODE)) && (target.nti.nai.btSak == supported_tags[i].SAK) && (!supported_tags[i].ATS_min_length || ((target.nti.nai.szAtsLen >= supported_tags[i].ATS_min_length) && @@ -73,6 +79,9 @@ freefare_tag_new (nfc_device *device, nfc_target target) /* Allocate memory for the found MIFARE target */ switch (tag_info->type) { + case FELICA: + tag = felica_tag_new (); + break; case MIFARE_CLASSIC_1K: case MIFARE_CLASSIC_4K: tag = mifare_classic_tag_new (); @@ -162,6 +171,26 @@ freefare_get_tags (nfc_device *device) } } + // Poll for a FELICA tag + modulation.nmt = NMT_FELICA; + modulation.nbr = NBR_424; // FIXME NBR_212 should also be supported + if ((candidates_count = nfc_initiator_list_passive_targets(device, modulation, candidates, MAX_CANDIDATES)) < 0) + return NULL; + + for (int c = 0; c < candidates_count; c++) { + FreefareTag t; + if ((t = freefare_tag_new(device, candidates[c]))) { + /* (Re)Allocate memory for the found FELICA targets array */ + FreefareTag *p = realloc (tags, (tag_count + 2) * sizeof (FreefareTag)); + if (p) + tags = p; + else + return tags; // FAIL! Return what has been found so far. + tags[tag_count++] = t; + tags[tag_count] = NULL; + } + } + return tags; } @@ -189,10 +218,27 @@ freefare_get_tag_friendly_name (FreefareTag tag) char * freefare_get_tag_uid (FreefareTag tag) { - char *res; - if ((res = malloc (2 * tag->info.nti.nai.szUidLen + 1))) { - for (size_t i =0; i < tag->info.nti.nai.szUidLen; i++) - snprintf (res + 2*i, 3, "%02x", tag->info.nti.nai.abtUid[i]); + char *res = NULL; + switch (tag->info.nm.nmt) { + case NMT_FELICA: + if ((res = malloc (16))) { + for (size_t i = 0; i < 8; i++) + snprintf (res + 2*i, 3, "%02x", tag->info.nti.nfi.abtId[i]); + } + break; + case NMT_ISO14443A: + if ((res = malloc (2 * tag->info.nti.nai.szUidLen + 1))) { + for (size_t i = 0; i < tag->info.nti.nai.szUidLen; i++) + snprintf (res + 2*i, 3, "%02x", tag->info.nti.nai.abtUid[i]); + } + break; + case NMT_DEP: + case NMT_ISO14443B2CT: + case NMT_ISO14443B2SR: + case NMT_ISO14443B: + case NMT_ISO14443BI: + case NMT_JEWEL: + res = strdup ("UNKNOWN"); } return res; } @@ -213,6 +259,9 @@ freefare_free_tag (FreefareTag tag) { if (tag) { switch (tag->tag_info->type) { + case FELICA: + felica_tag_free (tag); + break; case MIFARE_CLASSIC_1K: case MIFARE_CLASSIC_4K: mifare_classic_tag_free (tag); diff --git a/libfreefare/freefare.h b/libfreefare/freefare.h index 6a6183c..ca89a40 100644 --- a/libfreefare/freefare.h +++ b/libfreefare/freefare.h @@ -29,6 +29,7 @@ #endif // __cplusplus enum freefare_tag_type { + FELICA, // MIFARE_MINI, MIFARE_CLASSIC_1K, MIFARE_CLASSIC_4K, @@ -66,6 +67,17 @@ const char *freefare_strerror (FreefareTag tag); int freefare_strerror_r (FreefareTag tag, char *buffer, size_t len); void freefare_perror (FreefareTag tag, const char *string); +#define FELICA_SC_RW 0x0009 +#define FELICA_SC_RO 0x000b + +int felica_connect (FreefareTag tag); +int felica_disconnect (FreefareTag tag); + +ssize_t felica_read (FreefareTag tag, uint16_t service, uint8_t block, uint8_t *data, size_t length); +ssize_t felica_read_ex (FreefareTag tag, uint16_t service, uint8_t block_count, uint8_t blocks[], uint8_t *data, size_t length); +ssize_t felica_write (FreefareTag tag, uint16_t service, uint8_t block, uint8_t *data, size_t length); +ssize_t felica_write_ex (FreefareTag tag, uint16_t service, uint8_t block_count, uint8_t blocks[], uint8_t *data, size_t length); + int mifare_ultralight_connect (FreefareTag tag); int mifare_ultralight_disconnect (FreefareTag tag); diff --git a/libfreefare/freefare_internal.h b/libfreefare/freefare_internal.h index 6df3f63..b7779a2 100644 --- a/libfreefare/freefare_internal.h +++ b/libfreefare/freefare_internal.h @@ -102,6 +102,8 @@ struct mad_sector_0x00; struct mad_sector_0x10; void nxp_crc (uint8_t *crc, const uint8_t value); +FreefareTag felica_tag_new (void); +void felica_tag_free (FreefareTag tag); FreefareTag mifare_classic_tag_new (void); void mifare_classic_tag_free (FreefareTag tag); FreefareTag mifare_desfire_tag_new (void); @@ -189,6 +191,10 @@ struct freefare_tag { int active; }; +struct felica_tag { + struct freefare_tag __tag; +}; + struct mifare_classic_tag { struct freefare_tag __tag; @@ -262,6 +268,7 @@ struct mifare_ultralight_tag { #define ASSERT_ACTIVE(tag) do { if (!tag->active) return errno = ENXIO, -1; } while (0) #define ASSERT_INACTIVE(tag) do { if (tag->active) return errno = ENXIO, -1; } while (0) +#define ASSERT_FELICA(tag) do { if (tag->tag_info->type != FELICA) return errno = ENODEV, -1; } while (0) #define ASSERT_MIFARE_CLASSIC(tag) do { if ((tag->tag_info->type != MIFARE_CLASSIC_1K) && (tag->tag_info->type != MIFARE_CLASSIC_4K)) return errno = ENODEV, -1; } while (0) #define ASSERT_MIFARE_DESFIRE(tag) do { if (tag->tag_info->type != MIFARE_DESFIRE) return errno = ENODEV, -1; } while (0) #define IS_MIFARE_ULTRALIGHT_C(tag) (tag->tag_info->type == MIFARE_ULTRALIGHT_C) @@ -274,6 +281,7 @@ struct mifare_ultralight_tag { * This macros are intended to provide a convenient way to cast abstract * FreefareTag structures to concrete Tags (e.g. MIFARE Classic tag). */ +#define FELICA(tag) ((struct felica_tag *) tag) #define MIFARE_CLASSIC(tag) ((struct mifare_classic_tag *) tag) #define MIFARE_DESFIRE(tag) ((struct mifare_desfire_tag *) tag) #define MIFARE_ULTRALIGHT(tag) ((struct mifare_ultralight_tag *) tag) diff --git a/test/Makefile.am b/test/Makefile.am index e4e3151..56a2d04 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -11,6 +11,7 @@ TESTS = run-test.sh TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER="$(CUTTER)" cutter_unit_test_libs = \ + test_felica.la \ test_mad.la \ test_mifare_application.la \ test_mifare_classic.la \ @@ -33,6 +34,11 @@ endif AM_LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined +test_felica_la_SOURCES = test_felica.c \ + felica_fixture.c \ + felica_fixture.h +test_felica_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la + test_mad_la_SOURCES = test_mad.c test_mad_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la diff --git a/test/felica_fixture.c b/test/felica_fixture.c new file mode 100644 index 0000000..3beb9fd --- /dev/null +++ b/test/felica_fixture.c @@ -0,0 +1,82 @@ +/*- + * Copyright (C) 2015, Romain Tartiere + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +#include +#include + +#include "felica_fixture.h" + +static nfc_context *context; +static nfc_device *device = NULL; +static FreefareTag *tags = NULL; +FreefareTag tag = NULL; + +void +cut_setup (void) +{ + int res; + nfc_connstring devices[8]; + size_t device_count; + + nfc_init (&context); + cut_assert_not_null (context, cut_message ("Unable to init libnfc (malloc)")); + + device_count = nfc_list_devices (context, devices, 8); + if (device_count <= 0) + cut_omit ("No device found"); + + for (size_t i = 0; i < device_count; i++) { + device = nfc_open (context, devices[i]); + if (!device) + cut_omit ("nfc_open() failed."); + + tags = freefare_get_tags (device); + cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); + + tag = NULL; + for (int i=0; tags[i]; i++) { + if (freefare_get_tag_type(tags[i]) == FELICA) { + tag = tags[i]; + res = felica_connect (tag); + cut_assert_equal_int (0, res, cut_message ("felica_connect() failed")); + return; + } + } + nfc_close (device); + device = NULL; + freefare_free_tags (tags); + tags = NULL; + } + cut_omit ("No FeliCa tag on NFC device"); +} + +void +cut_teardown (void) +{ + if (tag) + mifare_classic_disconnect (tag); + + if (tags) { + freefare_free_tags (tags); + tags = NULL; + } + + if (device) + nfc_close (device); + + nfc_exit (context); +} diff --git a/test/felica_fixture.h b/test/felica_fixture.h new file mode 100644 index 0000000..3548d26 --- /dev/null +++ b/test/felica_fixture.h @@ -0,0 +1,18 @@ +/*- + * Copyright (C) 2015, Romain Tartiere. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +extern FreefareTag tag; diff --git a/test/test_felica.c b/test/test_felica.c new file mode 100644 index 0000000..d6c21cc --- /dev/null +++ b/test/test_felica.c @@ -0,0 +1,38 @@ +#include + +#include + +#include "felica_fixture.h" + +void +test_felica_read_without_encryption (void) +{ + uint8_t buffer[64]; + + int res = felica_read (tag, FELICA_SC_RO, 0x00, buffer, 16); + cut_assert_equal_int (16, res); + + uint8_t blocks[] = { + 0x02, + 0x03, + 0x04, + }; + + res = felica_read_ex (tag, FELICA_SC_RO, 3, blocks, buffer, 3 * 16); + cut_assert_equal_int (3 * 16, res); +} + +void +test_felica_write_without_encryption (void) +{ + uint8_t buffer[16] = { + 0x00, 0x01, 0x02, 0x03, + 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, + }; + + int res = felica_write (tag, FELICA_SC_RW, 0x0a, buffer, sizeof (buffer)); + + cut_assert_equal_int (0, res); +}