diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 469fea8..8b3c47c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -8,6 +8,7 @@ SET(EXAMPLES-SOURCES nfc-mfsetuid nfc-poll nfc-relay + nfc-st25tb pn53x-diagnose pn53x-sam pn53x-tamashell diff --git a/examples/Makefile.am b/examples/Makefile.am index d7018ac..089c7c8 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -10,6 +10,7 @@ bin_PROGRAMS = \ nfc-mfsetuid \ nfc-poll \ nfc-relay \ + nfc-st25tb \ pn53x-diagnose \ pn53x-sam @@ -63,6 +64,9 @@ nfc_mfsetuid_SOURCES = nfc-mfsetuid.c nfc_mfsetuid_LDADD = $(top_builddir)/libnfc/libnfc.la \ $(top_builddir)/utils/libnfcutils.la +nfc_st25tb_SOURCES = nfc-st25tb.c +nfc_st25tb_LDADD = $(top_builddir)/libnfc/libnfc.la + pn53x_diagnose_SOURCES = pn53x-diagnose.c pn53x_diagnose_LDADD = $(top_builddir)/libnfc/libnfc.la \ $(top_builddir)/utils/libnfcutils.la diff --git a/examples/nfc-st25tb.c b/examples/nfc-st25tb.c new file mode 100644 index 0000000..8468d17 --- /dev/null +++ b/examples/nfc-st25tb.c @@ -0,0 +1,609 @@ +/*- + * Free/Libre Near Field Communication (NFC) library + * + * Libnfc historical contributors: + * Copyright (C) 2009 Roel Verdult + * Copyright (C) 2009-2013 Romuald Conty + * Copyright (C) 2010-2012 Romain Tartière + * Copyright (C) 2010-2013 Philippe Teuwen + * Copyright (C) 2012-2013 Ludovic Rousseau + * See AUTHORS file for a more comprehensive list of contributors. + * Additional contributors of this file: + * + * 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, + * this list of conditions and the following disclaimer. + * 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. + * + * Note that this license only applies on the examples, NFC library itself is under LGPL + * + */ + +/** + * @file nfc-st25tb.c + * @brief Tool to operate on ISO-14443-B ST25TB* and legacy SR* cards + */ + +/* Benjamin DELPY `gentilkiwi` + * https://blog.gentilkiwi.com + * benjamin@gentilkiwi.com + * Licence : https://creativecommons.org/licenses/by/4.0/ + * Rely on : libnfc - https://github.com/nfc-tools/libnfc + * + * $ gcc -Wall -lnfc -o nfc-st25tb nfc-st25tb.c + * $ ./nfc-st25tb -h + * + * Tested with + * - ST25TB512-AC - (BE/Brussels/STIB ; AliExpress ones) + * - ST25TB512-AT - (FR/Lille/Ilevia ; FR/Reims/Citura) + * - SRT512 - legacy - (FR/Bordeaux/TBM) + * - SRI512 - legacy - (anonymous vending machine) + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include +#include + +#if defined(WIN32) /* mingw compiler */ +#include +#endif + +#define ST25TB_SR_BLOCK_MAX_SIZE 4 // for static arrays +typedef void(*get_info_specific) (uint8_t * systemArea); + +typedef struct _st_data { + uint8_t chipId; + bool bIsLegacy; + const char *szName; + const char *szDatasheetUrl; + uint8_t blockSize; + uint8_t nbNormalBlock; + uint8_t bnSystem; + get_info_specific pfnGetInfo; +} st_data; + +bool get_block_at(nfc_device *pnd, uint8_t block, uint8_t *data, uint8_t cbData, bool bPrintIt); +bool set_block_at(nfc_device *pnd, uint8_t block, uint8_t *data, uint8_t cbData, bool bPrintIt); +bool set_block_at_confirmed(nfc_device *pnd, uint8_t block, uint8_t *data, uint8_t cbData, bool bPrintIt); +const st_data * get_info(const nfc_target *pnt, bool bPrintIt); +void display_system_info(nfc_device *pnd, const st_data * stdata); +void print_hex(const uint8_t *pbtData, const size_t szBytes); + +int main(int argc, char *argv[]) +{ + nfc_context *context = NULL; + nfc_device *pnd = NULL; + nfc_target nt = {0}; + nfc_modulation nm = {NMT_ISO14443B, NBR_106}; + const st_data * stcurrent; + int opt, res; + bool bIsBlock = false, bIsRead = false, bIsWrite = false, bIsBadCli = false; + uint8_t i, blockNumber = 0, data[ST25TB_SR_BLOCK_MAX_SIZE] = {0xff, 0xff, 0xff, 0xff}; // just in case... + size_t cbData = 0; + + while(!bIsBadCli && ((opt = getopt(argc, argv, ":hib:rw:")) != -1)) + { + switch(opt) + { + case 'i': + + break; + + case 'b': + if(optarg) + { + bIsBlock = true; + blockNumber = strtoul(optarg, NULL, 0); + } + else bIsBadCli = true; + + break; + + case 'r': + bIsRead = true; + + break; + + case 'w': + if(optarg) + { + cbData = strlen(optarg); + if((cbData == (2*2)) || ((cbData == (4*2)))) + { + cbData >>= 1; + if(cbData == 2) // sr176 + { + res = sscanf(optarg, "%02hhx%02hhx", data, data + 1); + } + else // all others + { + res = sscanf(optarg, "%02hhx%02hhx%02hhx%02hhx", data, data + 1, data + 2, data + 3); + } + + if(res == (int) cbData) + { + bIsWrite = true; + } + } + + if(!bIsWrite) + { + bIsBadCli = true; + } + } + + break; + + default: // includes -h + bIsBadCli = true; + + } + } + + if(!bIsBadCli) + { + if(bIsBlock && (bIsRead || bIsWrite)) + { + if(bIsRead && bIsWrite) + { + printf("|mode : read then write\n"); + } + else if(bIsRead) + { + printf("|mode : read\n"); + } + else if(bIsWrite) + { + printf("|mode : write\n"); + } + + printf("|blk num: 0x%02hhx\n", blockNumber); + if(bIsWrite) + { + printf("|data : "); + print_hex(data, cbData); + printf("\n"); + } + } + else if(!bIsRead && !bIsWrite && !bIsBlock) + { + printf("|mode : info\n"); + } + else bIsBadCli = true; + } + + if(!bIsBadCli) + { + nfc_init(&context); + if(context) + { + pnd = nfc_open(context, NULL); + if(pnd) + { + res = nfc_initiator_init(pnd); + if(res == NFC_SUCCESS) + { + printf("Reader : %s - via %s\n ...wait for card...\n", nfc_device_get_name(pnd), nfc_device_get_connstring(pnd)); + + res = nfc_initiator_list_passive_targets(pnd, nm, &nt, 1); + if(res == 0) // we don't really wanted a NMT_ISO14443B + { + nm.nmt = NMT_ISO14443B2SR; // we want a NMT_ISO14443B2SR, but needed to ask for NMT_ISO14443B before + if (nfc_initiator_select_passive_target(pnd, nm, NULL, 0, &nt) > 0) + { + stcurrent = get_info(&nt, true); + if(stcurrent) + { + printf("\n"); + + if(bIsBlock && (bIsRead || bIsWrite)) + { + if(bIsRead) + { + get_block_at(pnd, blockNumber, NULL, 0, true); + } + + if(bIsWrite) + { + set_block_at_confirmed(pnd, blockNumber, data, cbData, true); + } + } + else if(!bIsRead && !bIsWrite && !bIsBlock) + { + for(i = 0; i < stcurrent->nbNormalBlock; i++) + { + get_block_at(pnd, i, NULL, 0, true); + } + display_system_info(pnd, stcurrent); + } + } + } + } + else if(res > 0) + { + printf("ERROR - We got a NMT_ISO14443B ?\n"); + } + else printf("ERROR - nfc_initiator_list_passive_targets: %i\n", res); + } + else printf("ERROR - nfc_initiator_init: %i\n", res); + + nfc_close(pnd); + } + else printf("ERROR - nfc_open\n"); + + nfc_exit(context); + } + else printf("ERROR - nfc_init\n"); + } + else + { + printf( + "Usage:\n" + " %s [-i]\n" + " %s -b N -r\n" + " %s -b N [-r] -w ABCD[EF01]\n %s -h\n" + "Options:\n" + " -i (default) information mode - will try to dump the tag content and display informations\n" + " -b N specify block number to operate on (tag dependent), needed for read (-r) and write (-w) modes\n" + " -r read mode - will try to read block (specified with -b N parameter)\n" + " -w ABCD[EF01] write mode - will try to write specicied data (2 or 4 bytes depending on tag) to block (specified with -b N parameter)\n" + " -h this help\n" + "Examples:\n" + " %s -i\n" + " Display all tag informations\n" + " %s -b 0x0e -r\n" + " Read block 0x0e (14) of the tag\n" + " %s -b 0x0d -w 0123abcd\n" + " Write block 0x0d (13) of the tag with hexadecimal value '01 23 ab cd'\n" + " %s -b 0x0c -r -w 0123abcd\n" + " Read, then write block 0x0c (12) of the tag with hexadecimal value '01 23 ab cd'\n" + "Warnings:\n" + " Be careful with: system area, counters & otp, bytes order.\n" + , argv[0], argv[0], argv[0], argv[0], argv[0], argv[0], argv[0], argv[0]); + } + + return 0; +} + +bool get_block_at(nfc_device *pnd, uint8_t block, uint8_t *data, uint8_t cbData, bool bPrintIt) +{ + bool bRet = false; + uint8_t tx[2] = {0x08, block}, rx[ST25TB_SR_BLOCK_MAX_SIZE]; // 4 is the maximum, SR176 (only 2) will fit + int res; + + res = nfc_initiator_transceive_bytes(pnd, tx, sizeof(tx), rx, sizeof(rx), 0); + if((res == 2) || (res == 4)) + { + if(data) + { + if(cbData == res) + { + memcpy(data, rx, res); + bRet = true; + } + else printf("ERROR - We got %i bytes for a %hhu buffer size?\n", res, cbData); + } + else bRet = true; + + if(bPrintIt) + { + printf("[0x%02hhx] ", block); + print_hex(rx, res); + printf("\n"); + } + } + else if(res > 0) + { + printf("ERROR - We got %i bytes?\n", res); + } + else printf("ERROR - nfc_initiator_transceive_bytes(get): %i\n", res); + + return bRet; +} + +bool set_block_at(nfc_device *pnd, uint8_t block, uint8_t *data, uint8_t cbData, bool bPrintIt) +{ + bool bRet = false; + uint8_t tx[2 + ST25TB_SR_BLOCK_MAX_SIZE] = {0x09, block}; // 4 is the maximum, SR176 (only 2) will fit + int res; + + if(cbData <= ST25TB_SR_BLOCK_MAX_SIZE) + { + memcpy(tx + 2, data, cbData); + + if(bPrintIt) + { + printf(">0x%02hhx> ", block); + print_hex(data, cbData); + printf("\n"); + } + + res = nfc_initiator_transceive_bytes(pnd, tx, 2 + cbData, NULL, 0, 0); + if(res == NFC_ERFTRANS) // ? :') + { + bRet = true; + } + else printf("ERROR - nfc_initiator_transceive_bytes(set): %i\n", res); + } + else printf("ERROR - Wanted to write %hhu bytes, but maximum is %hhu\n", cbData, ST25TB_SR_BLOCK_MAX_SIZE); + + return bRet; +} + +bool set_block_at_confirmed(nfc_device *pnd, uint8_t block, uint8_t *data, uint8_t cbData, bool bPrintIt) +{ + bool bRet = false; + uint8_t buffer[ST25TB_SR_BLOCK_MAX_SIZE]; // maximum size will be checked in set_block_at + + if(set_block_at(pnd, block, data, cbData, bPrintIt)) + { + if(get_block_at(pnd, block, buffer, cbData, bPrintIt)) + { + if(memcmp(data, buffer, cbData) == 0) + { + bRet = true; + } + else if(bPrintIt) + { + printf("WARNING - not same value readed after write\n"); + } + } + } + + return bRet; +} + +void get_info_st25tb512(uint8_t * systemArea) +{ + uint8_t b, i; + + b = ((*(uint32_t *) systemArea) >> 15) & 1; + + printf(" | ST reserved : "); + for(i = 0; i < 15; i++) + { + printf("%hhu", ((*(uint32_t *) systemArea) >> i) & 1); + } + printf("\n | b15 : %hhu - %sOTP (?)\n | OTP_Lock_Reg : ", b, b ? "not " : ""); + for(i = 16; i < 32; i++) + { + printf("%hhu", ((*(uint32_t *) systemArea) >> i) & 1); + } + printf("\n"); + for(i = 16; i < 32; i++) + { + if(!(((*(uint32_t *) systemArea) >> i) & 1)) + { + printf(" block 0x%02hhx is write protected\n", i - 16); + } + } +} + +void get_info_st25tb2k_4k(uint8_t * systemArea) +{ + uint8_t b, i; + + b = ((*(uint32_t *) systemArea) >> 15) & 1; + + printf(" | ST reserved : "); + for(i = 0; i < 15; i++) + { + printf("%hhu", ((*(uint32_t *) systemArea) >> i) & 1); + } + printf("\n | b15 : %hhu - %sOTP (?)\n | OTP_Lock_RegU: ", b, b ? "not " : ""); + for(i = 16; i < 24; i++) + { + printf("%hhu", ((*(uint32_t *) systemArea) >> i) & 1); + } + printf("\n | OTP_Lock_Reg : "); + for(i = 24; i < 32; i++) + { + printf("%hhu", ((*(uint32_t *) systemArea) >> i) & 1); + } + printf("\n"); + if(!(((*(uint32_t *) systemArea) >> 24) & 1)) + { + printf(" blocks 0x07 and 0x08 are write protected\n"); + } + for(i = 25; i < 32; i++) + { + if(!(((*(uint32_t *) systemArea) >> i) & 1)) + { + printf(" block 0x%02hhx is write protected\n", i - 16); + } + } +} + +void get_info_sr176_legacy(uint8_t * systemArea) +{ + uint8_t i; + + printf(" | Fixed Chip_ID: 0x%1x\n | ST reserved : ", systemArea[0] & 0x0f); + for(i = 4; i < 8; i++) + { + printf("%hhu", ((*(uint16_t *) systemArea) >> i) & 1); + } + printf("\n | OTP_Lock_Reg : "); + for(i = 8; i < 16; i++) + { + printf("%hhu", ((*(uint16_t *) systemArea) >> i) & 1); + } + printf("\n"); + for(i = 8; i < 16; i++) + { + if(((*(uint16_t *) systemArea) >> i) & 1) + { + printf(" blocks 0x%02hhx and 0x%02hhx are write protected\n", (i - 8) * 2, ((i - 8) * 2) + 1); + } + } +} + +void get_info_sri_srt_512_legacy(uint8_t * systemArea) +{ + uint8_t b, i; + + b = ((*(uint32_t *) systemArea) >> 15) & 1; + + printf(" | Fixed Chip_ID: 0x%02hhx\n | ST reserved : ", systemArea[0]); + for(i = 8; i < 15; i++) + { + printf("%hhu", ((*(uint32_t *) systemArea) >> i) & 1); + } + printf("\n | b15 : %hhu - %sOTP (?)\n | OTP_Lock_Reg : ", b, b ? "not " : ""); + for(i = 16; i < 32; i++) + { + printf("%hhu", ((*(uint32_t *) systemArea) >> i) & 1); + } + printf("\n"); + for(i = 16; i < 32; i++) + { + if(!(((*(uint32_t *) systemArea) >> i) & 1)) + { + printf(" block 0x%02hhx is write protected\n", i - 16); + } + } +} + +void get_info_sri2k_4k_srix4k_srix512_legacy(uint8_t * systemArea) +{ + uint8_t i; + + printf(" | Fixed Chip_ID: 0x%02hhx\n | ST reserved : ", systemArea[0]); + for(i = 8; i < 24; i++) + { + printf("%hhu", ((*(uint32_t *) systemArea) >> i) & 1); + } + printf("\n | OTP_Lock_Reg : "); + for(i = 24; i < 32; i++) + { + printf("%hhu", ((*(uint32_t *) systemArea) >> i) & 1); + } + printf("\n"); + if(!(((*(uint32_t *) systemArea) >> 24) & 1)) + { + printf(" blocks 0x07 and 0x08 are write protected\n"); + } + for(i = 25; i < 32; i++) + { + if(!(((*(uint32_t *) systemArea) >> i) & 1)) + { + printf(" block 0x%02hhx is write protected\n", i - 16); + } + } +} + +const st_data STRefs[] = { + {0x1b, false, "ST25TB512-AC", "https://www.st.com/resource/en/datasheet/st25tb512-ac.pdf", 4, 16, 255, get_info_st25tb512}, + {0x33, false, "ST25TB512-AT", "https://www.st.com/resource/en/datasheet/st25tb512-at.pdf", 4, 16, 255, get_info_st25tb512}, + {0x3f, false, "ST25TB02K", "https://www.st.com/resource/en/datasheet/st25tb02k.pdf", 4, 64, 255, get_info_st25tb2k_4k}, + {0x1f, false, "ST25TB04K", "https://www.st.com/resource/en/datasheet/st25tb04k.pdf", 4, 128, 255, get_info_st25tb2k_4k}, +}; +const st_data STRefs_legacy[] = { + { 0, true, "SRI4K(s)", NULL, 4, 128, 255, NULL}, + { 2, true, "SR176", "https://www.st.com/resource/en/datasheet/sr176.pdf", 2, 15, 15, get_info_sr176_legacy}, + { 3, true, "SRIX4K", NULL, 4, 128, 255, get_info_sri2k_4k_srix4k_srix512_legacy}, + { 4, true, "SRIX512", "https://www.st.com/resource/en/datasheet/srix512.pdf", 4, 16, 255, get_info_sri2k_4k_srix4k_srix512_legacy}, + { 6, true, "SRI512", "https://www.st.com/resource/en/datasheet/sri512.pdf", 4, 16, 255, get_info_sri_srt_512_legacy}, + { 7, true, "SRI4K", "https://www.st.com/resource/en/datasheet/sri4k.pdf", 4, 128, 255, get_info_sri2k_4k_srix4k_srix512_legacy}, + {12, true, "SRT512", "https://www.st.com/resource/en/datasheet/srt512.pdf", 4, 16, 255, get_info_sri_srt_512_legacy}, + {15, true, "SRI2K", "https://www.st.com/resource/en/datasheet/sri2k.pdf", 4, 64, 255, get_info_sri2k_4k_srix4k_srix512_legacy}, +}; + +const st_data * get_info(const nfc_target *pnt, bool bPrintIt) +{ + const st_data *currentData = NULL; + const uint8_t *p; + uint8_t chipId, i; + + if(pnt->nm.nmt == NMT_ISO14443B2SR) + { + printf("Target : %s (%s)\nUID : ", str_nfc_modulation_type(pnt->nm.nmt), str_nfc_baud_rate(pnt->nm.nbr)); + print_hex(pnt->nti.nsi.abtUID, sizeof(pnt->nti.nsi.abtUID)); + printf("\n"); + + p = pnt->nti.nsi.abtUID; + if(p[7] == 0xd0) // ST25TB* / SR* + { + chipId = p[5]; + printf("Manuf : 0x%02hhx - %s\n", p[6], (p[6] == 0x02) ? "STMicroelectronics" : "other"); + + for(i = 0; i < (sizeof(STRefs) / sizeof(STRefs[0])); i++) + { + if(chipId == STRefs[i].chipId) + { + currentData = &STRefs[i]; + break; + } + } + + if(!currentData) + { + chipId >>= 2; + for(i = 0; i < (sizeof(STRefs_legacy) / sizeof(STRefs_legacy[0])); i++) + { + if(chipId == STRefs_legacy[i].chipId) + { + currentData = &STRefs_legacy[i]; + break; + } + } + } + + if(bPrintIt && currentData) + { + printf("ChipId : 0x%02hhx - %s%s\nSerial : 0x", currentData->chipId, currentData->szName, currentData->bIsLegacy ? " (legacy)" : ""); + if(currentData->bIsLegacy) + { + printf("%1hhx", p[5] & 0x03); + } + printf("%02hhx%02hhx%02hhx%02hhx%02hhx\n|blk sz : %hhu bits\n|nb blks: %hhu\n|sys idx: %hhu\n", p[4], p[3], p[2], p[1], p[0], currentData->blockSize * 8, currentData->nbNormalBlock, currentData->bnSystem); + } + } + else printf("WARNI - Last byte of UID isn\'t 0xd0, but 0x%02hhx (not ST25TB / SR series?)\n", p[7]); + } + else printf("ERROR - not a NMT_ISO14443B2SR ?\n"); + + return currentData; +} + +void display_system_info(nfc_device *pnd, const st_data * stdata) +{ + uint8_t systemArea[ST25TB_SR_BLOCK_MAX_SIZE]; + + if(get_block_at(pnd, stdata->bnSystem, systemArea, stdata->blockSize, true)) + { + if(stdata->pfnGetInfo) + { + stdata->pfnGetInfo(systemArea); + } + } +} + +void print_hex(const uint8_t *pbtData, const size_t szBytes) +{ + size_t szPos; + for (szPos = 0; szPos < szBytes; szPos++) + { + printf("%02hhx ", pbtData[szPos]); + } +} \ No newline at end of file