From 4d2ece3ea25d8cb240dd328d77e0e001fde5e26c Mon Sep 17 00:00:00 2001 From: GOB Date: Mon, 5 Jan 2026 11:16:56 +0900 Subject: [PATCH] Move the processing to the layer side(NFCF) --- .../UnitUnified/NFCF/Emulation/Emulation.ino | 20 ++ .../NFCF/Emulation/main/Emulation.cpp | 200 +++++++++++ .../main/JapanTransportationICCard.cpp | 16 +- examples/UnitUnified/NFCF/NDEF/main/NDEF.cpp | 8 +- .../NFCF/ReadWriteMAC/main/ReadWriteMAC.cpp | 252 ++++++++----- src/nfc/layer/ndef_layer.cpp | 4 +- src/nfc/layer/nfc_layer_f.cpp | 338 ++++++++++++++---- src/nfc/layer/nfc_layer_f.hpp | 73 ++-- src/nfc/layer/unit/nfc_layer_f_ST25R3916.cpp | 52 +-- src/unit/unit_ST25R3916.hpp | 114 ++---- src/unit/unit_ST25R3916_nfcf.cpp | 56 +-- 11 files changed, 760 insertions(+), 373 deletions(-) create mode 100644 examples/UnitUnified/NFCF/Emulation/Emulation.ino create mode 100644 examples/UnitUnified/NFCF/Emulation/main/Emulation.cpp diff --git a/examples/UnitUnified/NFCF/Emulation/Emulation.ino b/examples/UnitUnified/NFCF/Emulation/Emulation.ino new file mode 100644 index 0000000..9c09f7d --- /dev/null +++ b/examples/UnitUnified/NFCF/Emulation/Emulation.ino @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example using M5UnitUnified for ST25R3916 + NFC-F Emulation mode +*/ +// ************************************************************* +// Choose one define symbol to match the unit you are using +// ************************************************************* +#if !defined(USING_UNIT_NFC) && !defined(USING_HACKER_CAP) +// For UnitNFC +// #define USING_UNIT_NFC +// For CapNFC +// #define USING_HACKER_CAP +#endif +#include "main/Emulation.cpp" + diff --git a/examples/UnitUnified/NFCF/Emulation/main/Emulation.cpp b/examples/UnitUnified/NFCF/Emulation/main/Emulation.cpp new file mode 100644 index 0000000..9d52a5e --- /dev/null +++ b/examples/UnitUnified/NFCF/Emulation/main/Emulation.cpp @@ -0,0 +1,200 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example using M5UnitUnified for ST25R3916 + NFC-F Emulation mode +*/ +#include +#include +#include +#include +#include + +// ************************************************************* +// Choose one define symbol to match the unit you are using +// ************************************************************* +#if !defined(USING_UNIT_NFC) && !defined(USING_HACKER_CAP) +// For UnitNFC +// #define USING_UNIT_NFC +// For CapNFC +// #define USING_HACKER_CAP +#endif + +using namespace m5::nfc; +using namespace m5::nfc::f; + +namespace { +auto& lcd = M5.Display; +m5::unit::UnitUnified Units; + +#if defined(USING_UNIT_NFC) +#pragma message "Choose UnitNFC" +m5::unit::UnitNFC unit{}; // I2C +#elif defined(USING_HACKER_CAP) +#pragma message "Choose HackerCapNFC" +m5::unit::HackerCapNFC unit{}; // HackerCap (SPI) +#else +#error Choose unit please! +#endif +m5::nfc::EmulationLayerF emu_f{unit}; + +PICC picc{}; + +constexpr Type type{Type::FeliCaLiteS}; +// constexpr uint8_t IDm[8] = {0x01, 0x2E, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; // See also SONY speficication +// documents +constexpr uint8_t IDm[8] = {0x01, 0x2E, 0x50, 0xE5, 0x3C, 0x4B, 0x4F, 0x29}; +constexpr uint8_t PMm[8] = {0x00, 0xF1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00}; // See also SONY speficication documents +uint8_t picc_memory[] = { + 0x10, 0x04, 0x01, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x58, 0x00, 0x7B, // S_PAD0 + 0x91, 0x01, 0x0D, 0x55, 0x04, 0x6D, 0x35, 0x73, 0x74, 0x61, 0x63, 0x6B, 0x2E, 0x63, 0x6F, 0x6D, // 1 + 0x2F, 0x11, 0x01, 0x10, 0x54, 0x02, 0x65, 0x6E, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x4D, 0x35, // 2 + 0x53, 0x74, 0x61, 0x63, 0x6B, 0x11, 0x01, 0x1A, 0x54, 0x02, 0x6A, 0x61, 0xE3, 0x81, 0x93, 0xE3, // 3 + 0x82, 0x93, 0xE3, 0x81, 0xAB, 0xE3, 0x81, 0xA1, 0xE3, 0x81, 0xAF, 0x20, 0x4D, 0x35, 0x53, 0x74, // 4 + 0x61, 0x63, 0x6B, 0x51, 0x01, 0x11, 0x54, 0x02, 0x7A, 0x68, 0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD, // 5 + 0x20, 0x4D, 0x35, 0x53, 0x74, 0x61, 0x63, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // REG + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RC 0x80 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D_ID + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SER_C + 0x88, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SYS_C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CKV + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CK + 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MC + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // WCNT 0x90 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC_A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // STATE + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CRC_CHRCK 0xA0 +}; + +void embed_idm_pmm(uint8_t* mem, const PICC& picc) +{ + memcpy(mem + 17 * 16, picc.idm, 8); // ID + memcpy(mem + 18 * 16, picc.idm, 8); // D_ID + memcpy(mem + 18 * 16 + 8, picc.pmm, 8); // D_ID +} + +constexpr uint16_t color_table[] = { + // None, Off, Idle, Ready, Active, Halt }; + TFT_BLACK, TFT_RED, TFT_BLUE, TFT_YELLOW, TFT_GREEN, TFT_MAGENTA}; +constexpr const char* state_table[] = {"-", "O", "I", "R", "A", "H"}; + +} // namespace + +void setup() +{ + delay(1500); + + M5.begin(); + M5.setTouchButtonHeightByRatio(100); + + // Emulation settings + auto cfg = unit.config(); + cfg.emulation = true; + cfg.mode = NFC::F; + unit.config(cfg); + +#if 0 + //// M5GFX 0.2.15 NG! with HackerCap + auto board = M5.getBoard(); + if (board != lgfx::board_t::board_M5CardputerADV) { + M5_LOGE("This is NOT M5Cardputer-ADV %U/%XH", board, board); + lcd.fillScreen(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } +#endif + +#if defined(USING_UNIT_NFC) + auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda); + auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl); + M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl); + Wire.end(); + Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U); + // Wire.begin(pin_num_sda, pin_num_scl, 1000 * 1000u); + if (!Units.add(unit, Wire) || !Units.begin()) { + M5_LOGE("Failed to begin"); + lcd.clear(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } +#elif defined(USING_HACKER_CAP) + if (!SPI.bus()) { + auto spi_sclk = M5.getPin(m5::pin_name_t::sd_spi_sclk); + auto spi_mosi = M5.getPin(m5::pin_name_t::sd_spi_mosi); + auto spi_miso = M5.getPin(m5::pin_name_t::sd_spi_miso); + M5_LOGI("getPin: %d,%d,%d", spi_sclk, spi_mosi, spi_miso); + SPI.begin(spi_sclk, spi_miso, spi_mosi /* SS is shared SD, CC1101, ST25R3916 */); + } + + SPISettings settings = {10000000, MSBFIRST, SPI_MODE1}; + if (!Units.add(unit, SPI, settings) || !Units.begin()) { + M5_LOGE("Failed to begin"); + lcd.fillScreen(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } +#endif + M5_LOGI("M5UnitUnified has been begun"); + M5_LOGI("%s", Units.debugInfo().c_str()); + + if (lcd.width() < lcd.height()) { + lcd.setRotation(1); + } + lcd.setFont(&fonts::Font2); + + // + lcd.startWrite(); + lcd.fillScreen(TFT_RED); + if (picc.emulate(type, IDm, PMm)) { + embed_idm_pmm(picc_memory, picc); + if (emu_f.begin(picc, picc_memory, sizeof(picc_memory))) { + lcd.fillScreen(TFT_DARKGREEN); + lcd.setCursor(0, 16); + const auto& e_picc = emu_f.emulatePICC(); + M5.Log.printf("Emulation:%s %s:%s SC:%02X\n", e_picc.typeAsString().c_str(), e_picc.idmAsString().c_str(), + e_picc.pmmAsString().c_str(), e_picc.emulation_sc); + lcd.printf("Emulation:%s\nIDm:%s\nPMm:%s\nSC:%02X\n", e_picc.typeAsString().c_str(), + e_picc.idmAsString().c_str(), e_picc.pmmAsString().c_str(), e_picc.emulation_sc); + } else { + M5_LOGE("Start"); + } + } else { + M5_LOGE("PICC"); + } + lcd.fillRect(0, 0, 32, 16, color_table[0]); + lcd.drawString(state_table[0], 0, 0); + lcd.endWrite(); +} + +void loop() +{ + M5.update(); + Units.update(); + emu_f.update(); // Need call in loop + + static EmulationLayerF::State latest{}; + auto state = emu_f.state(); + if (latest != state) { + latest = state; + lcd.startWrite(); + lcd.fillRect(0, 0, 32, 16, color_table[m5::stl::to_underlying(state)]); + lcd.drawString(state_table[m5::stl::to_underlying(state)], 0, 0); + lcd.endWrite(); + } +} diff --git a/examples/UnitUnified/NFCF/JapanTransportationICCard/main/JapanTransportationICCard.cpp b/examples/UnitUnified/NFCF/JapanTransportationICCard/main/JapanTransportationICCard.cpp index 7c74ea7..fb93ff3 100644 --- a/examples/UnitUnified/NFCF/JapanTransportationICCard/main/JapanTransportationICCard.cpp +++ b/examples/UnitUnified/NFCF/JapanTransportationICCard/main/JapanTransportationICCard.cpp @@ -75,16 +75,27 @@ struct tm buf_to_tm(const uint8_t date[2], const uint8_t time[2] = nullptr) void dump_jtic() { + uint16_t sc[255]{}; + uint8_t sc_num{}; + if (!nfc_f.requestSystemCode(sc, sc_num)) { + return; + } + M5.Log.printf("System code %u\n", sc_num); + for (uint_fast8_t i = 0; i < sc_num; ++i) { + M5.Log.printf(" %04X\n", sc[i]); + } + standard::Mode m{}; if (!nfc_f.requestResponse(m)) { return; } + M5.Log.printf("Mode:%u\n", m); // Balance uint16_t key_version{}; if (nfc_f.requestService(key_version, service_balance) && key_version != 0xFFFF) { uint8_t buf[16]{}; - if (nfc_f.read16(buf, 0, service_balance)) { + if (nfc_f.read16(buf, block_t(0), service_balance)) { M5.Log.printf("Type:%02X Balance:%u Update:%u\n", buf[8], // ((uint16_t)buf[12] << 8) | buf[11], // LE @@ -240,6 +251,9 @@ void setup() } lcd.setFont(&fonts::Font0); lcd.fillScreen(0); + lcd.setCursor(0, 0); + lcd.printf("Please put the PICC and click BtnA"); + M5.Log.printf("Please put the PICC and click BtnA\n"); } void loop() diff --git a/examples/UnitUnified/NFCF/NDEF/main/NDEF.cpp b/examples/UnitUnified/NFCF/NDEF/main/NDEF.cpp index cf5b923..f8a0bd4 100644 --- a/examples/UnitUnified/NFCF/NDEF/main/NDEF.cpp +++ b/examples/UnitUnified/NFCF/NDEF/main/NDEF.cpp @@ -175,8 +175,8 @@ void setup() lcd.setFont(&fonts::Font0); lcd.fillScreen(0); lcd.setCursor(0, 0); - lcd.printf("Please put the PICC and click G0"); - M5.Log.printf("Please put the PICC and click G0\n"); + lcd.printf("Please put the PICC and click BtnA"); + M5.Log.printf("Please put the PICC and click BtnA\n"); } void loop() @@ -207,8 +207,8 @@ void loop() nfc_f.deactivate(); } lcd.setCursor(0, 0); - lcd.printf("Please put the PICC and click/hold G0"); - M5.Log.printf("Please put the PICC and click/hold G0\n"); + lcd.printf("Please put the PICC and click/hold BtnA"); + M5.Log.printf("Please put the PICC and click/hold BtnA\n"); } else { M5.Log.printf("PICC NOT exists\n"); } diff --git a/examples/UnitUnified/NFCF/ReadWriteMAC/main/ReadWriteMAC.cpp b/examples/UnitUnified/NFCF/ReadWriteMAC/main/ReadWriteMAC.cpp index e84734e..a8dc436 100644 --- a/examples/UnitUnified/NFCF/ReadWriteMAC/main/ReadWriteMAC.cpp +++ b/examples/UnitUnified/NFCF/ReadWriteMAC/main/ReadWriteMAC.cpp @@ -11,7 +11,7 @@ NOTICE: Please note that cards that have undergone the initial issuance procedure cannot be read without subsequent authentication. ******************************************************************************************************************* -*/ + */ #include #include #include @@ -111,7 +111,7 @@ bool internal_auth() } uint8_t ck[16]{}; - derive_card_key(ck, example_master_key, example_ckv, picc.idm.data()); + derive_card_key(ck, example_master_key, example_ckv, picc.idm); // M5.Log.printf("==== CK:\n"); // m5::utility::log::dump(ck, 16, false); @@ -134,7 +134,7 @@ bool external_auth() } uint8_t ck[16]{}; - derive_card_key(ck, example_master_key, example_ckv, picc.idm.data()); + derive_card_key(ck, example_master_key, example_ckv, picc.idm); // M5.Log.printf("==== CK:\n"); // m5::utility::log::dump(ck, 16, false); @@ -142,62 +142,166 @@ bool external_auth() return nfc_f.externalAuthenticate(ck, example_ckv); } -void read_example() +void access_example() { uint8_t rx[16]{}; + constexpr char data0[] = "0-ABCDEF"; + constexpr char data1[] = "1-GHIJKL"; + constexpr char data2[] = "2-MNOPQR"; - // nfc_f.dump(lite_s::MC); + nfc_f.dump(); - if (!nfc_f.read16(rx, 1)) { - M5.Log.printf("Failed to read 1 (need auth)\n"); + // No auth + M5.Log.printf("======== No auth\n"); + + M5.Log.printf("Block 2 does not require authentication)\n"); + if (nfc_f.write16(2, (uint8_t*)data2, sizeof(data2))) { + M5.Log.printf(" OK W\n"); + } + if (nfc_f.read16(rx, 2)) { + M5.Log.printf(" OK R\n"); + m5::utility::log::dump(rx, 16, false); + } + M5.Log.printf("Block 0 requires authentication\n"); + if (!nfc_f.write16(0, (uint8_t*)data0, sizeof(data0))) { + M5.Log.printf(" OK NW\n"); } else { - M5.Log.printf("read block 1 without auth OK\n"); - // m5::utility::log::dump(rx, 16, false); + M5_LOGE(" NG"); } if (!nfc_f.read16(rx, 0)) { - M5.Log.printf("Failed to read 0 (need auth)\n"); + M5.Log.printf(" OK NR\n"); } else { - M5.Log.printf("read block 0 without auth OK\n"); - // m5::utility::log::dump(rx, 16, false); + M5_LOGE(" NG"); } + M5.Log.printf("Block 1 requires authentication\n"); + if (!nfc_f.write16(1, (uint8_t*)data1, sizeof(data1))) { + M5.Log.printf(" OK NW\n"); + } else { + M5_LOGE(" NG"); + } + if (!nfc_f.read16(rx, 1)) { + M5.Log.printf(" OK NR\n"); + } else { + M5_LOGE(" NG"); + } + + // Internal auth if (!internal_auth()) { // ****************************************************************************** // If the first_issuance_procedure_lite_s() has not been executed, an error will occur here. // ****************************************************************************** - M5_LOGE("Failed to internalAuthenticate"); + M5_LOGE("Failed to internal authenticate"); lcd.fillScreen(TFT_RED); return; } - M5.Log.printf("internal auth OK\n"); + M5.Log.printf("======== Internal auth OK\n"); - // Read block - if (nfc_f.readWithMAC16(rx, 1)) { - M5.Log.printf("Read block 1 OK\n"); - m5::utility::log::dump(rx, 16, false); - } else { - M5_LOGE("Failed to read 1"); + M5.Log.printf("Block 2 does not require authentication)\n"); + if (nfc_f.read16(rx, 2)) { + M5.Log.printf(" OK\n"); } - - // Read block 0 - if (!external_auth()) { - M5_LOGE("Failed to externalAuthenticate"); - lcd.fillScreen(TFT_RED); - return; - } - M5.Log.printf("external auth OK\n"); - + M5.Log.printf("Block 0 requires authentication\n"); if (!nfc_f.read16(rx, 0)) { - M5.Log.printf("You can NOT read16"); + M5.Log.printf(" OK NR\n"); } else { - M5_LOGE("Oops!"); + M5_LOGE(" NG"); + } + if (!nfc_f.readWithMAC16(rx, 0)) { + M5.Log.printf(" OK NR\n"); + } else { + M5_LOGE(" NG"); + } + if (!nfc_f.write16(0, (uint8_t*)data0, sizeof(data0))) { + M5.Log.printf(" OK NW\n"); + } else { + M5_LOGE(" NG"); + } + if (!nfc_f.writeWithMAC16(0, (uint8_t*)data0, sizeof(data0))) { + M5.Log.printf(" OK NWMAC\n"); + } else { + M5_LOGE(" NG"); + } + + M5.Log.printf("Block 1 requires authentication\n"); + if (!nfc_f.read16(rx, 1)) { + M5.Log.printf(" OK NR\n"); + } else { + M5_LOGE(" NG"); + } + if (!nfc_f.readWithMAC16(rx, 1)) { + M5.Log.printf(" OK NR\n"); + } else { + M5_LOGE(" NG"); + } + if (!nfc_f.write16(1, (uint8_t*)data1, sizeof(data0))) { + M5.Log.printf(" OK NW\n"); + } else { + M5_LOGE(" NG"); + } + if (!nfc_f.writeWithMAC16(1, (uint8_t*)data1, sizeof(data0))) { + M5.Log.printf(" OK NWMAC\n"); + } else { + M5_LOGE(" NG"); + } + + // External auth + if (!external_auth()) { + M5_LOGE("Failed to external authenticate"); + lcd.fillScreen(TFT_RED); + return; + } + M5.Log.printf("======== External auth OK\n"); + + M5.Log.printf("Block 2 does not require authentication)\n"); + if (nfc_f.read16(rx, 2)) { + M5.Log.printf(" OK\n"); + } + + M5.Log.printf("Block 0 requires authentication\n"); + if (nfc_f.read16(rx, 0)) { + M5.Log.printf(" OK R\n"); m5::utility::log::dump(rx, 16, false); + } else { + M5_LOGE(" NG"); } if (nfc_f.readWithMAC16(rx, 0)) { - M5.Log.printf("Read block 0 OK\n"); + M5.Log.printf(" OK RMAC\n"); m5::utility::log::dump(rx, 16, false); } else { - M5_LOGE("Failed to read 0"); + M5_LOGE(" NG"); + } + // Block 0 can write without encryption + if (nfc_f.write16(0, (uint8_t*)data0, sizeof(data0))) { + M5.Log.printf(" OK W\n"); + } else { + M5_LOGE(" NG"); + } + + M5.Log.printf("Block 1 requires authentication\n"); + if (nfc_f.read16(rx, 1)) { + M5.Log.printf(" OK R\n"); + m5::utility::log::dump(rx, 16, false); + } else { + M5_LOGE(" NG"); + } + if (nfc_f.readWithMAC16(rx, 1)) { + M5.Log.printf(" OK RMAC\n"); + m5::utility::log::dump(rx, 16, false); + } else { + M5_LOGE(" NG"); + } + // Block 1 can NOT write without encryption + if (!nfc_f.write16(1, (uint8_t*)data1, sizeof(data1))) { + M5.Log.printf(" OK NW\n"); + } else { + M5_LOGE(" NG"); + } + // Block 1 write needs with MAC + if (nfc_f.writeWithMAC16(1, (uint8_t*)data1, sizeof(data1))) { + M5.Log.printf(" OK WMAC\n"); + } else { + M5_LOGE(" NG"); } lcd.fillScreen(0); @@ -210,11 +314,11 @@ bool first_issuance_procedue_lite_s(const PICC& picc, const uint8_t master_key[2 M5.Log.printf("First issuance procedue...\n"); // 7.3.2 Write ID (No DFC) - if (!nfc_f.write16(lite_s::ID, picc.idm.data(), 8) || !nfc_f.read16(rbuf, lite_s::ID)) { + if (!nfc_f.write16(lite_s::ID, picc.idm, 8) || !nfc_f.read16(rbuf, lite_s::ID)) { M5_LOGE("Failed to write/read ID"); return false; } - if (memcmp(rbuf, picc.idm.data(), 8) != 0) { + if (memcmp(rbuf, picc.idm, 8) != 0) { M5_LOGE("Failed to verify ID"); return false; } @@ -225,7 +329,7 @@ bool first_issuance_procedue_lite_s(const PICC& picc, const uint8_t master_key[2 uint8_t wbuf[2]{}; wbuf[0] = ckv >> 8; wbuf[1] = ckv & 0xFF; - derive_card_key(ck, master_key, ckv, picc.idm.data()); + derive_card_key(ck, master_key, ckv, picc.idm); // M5.Log.printf("==== CK:\n"); // m5::utility::log::dump(ck, 16, false); @@ -284,30 +388,34 @@ bool first_issuance_procedue_lite_s(const PICC& picc, const uint8_t master_key[2 M5_LOGE("Failed to read MC"); return false; } - // MC_SP_REG_W_MAC_A (Must be MC_SP_REG_W_RESTR,MC_SP_REG_R_RESTR bit on) - mc[10] = 0x01; // Write SPAD_0 needs MAC -> external auth - // MC_SP_REG_W_RESTR - mc[8] = 0x01; // Write SPAD_0,1 needs Auth - // MC_SP_REG_R_RESTR - mc[6] = 0x01; // Read SPAD_0,1 needs Auth - // MC_CKCKV_W_MAC_A - // mc[5] = 0x01; // Write CK and CKV needs MAC - // RF_PRM - mc[4] = 0x07; // Fixed value - // SYS_OP - mc[3] = 0x00; // 0x01 NDEF - // MC_ALL - mc[2] = 0xFF; + + constexpr uint8_t MC_STATE_W_MAC_A{12}; + constexpr uint8_t MC_SP_REG_W_MAC_A{10}; + constexpr uint8_t MC_SP_REG_W_RESTR{8}; + constexpr uint8_t MC_SP_REG_R_RESTR{6}; + constexpr uint8_t RF_PRM{4}; + constexpr uint8_t SYS_OP{3}; + constexpr uint8_t MC_ALL{2}; + + mc[MC_STATE_W_MAC_A] = 0x01; + mc[MC_SP_REG_W_MAC_A] = 0x02; // SPAD_1 write needs MAC + mc[MC_SP_REG_W_RESTR] = 0x01 | 0x02; // SPAD_0.1 write needs Auth + mc[MC_SP_REG_R_RESTR] = 0x01 | 0x02; // SPAD_0.1 read needs Auth + mc[RF_PRM] = 0x07; // Fixed value + mc[SYS_OP] = 0x00; // 0x01 NDEF + mc[MC_ALL] = 0xFF; // RO // Some blocks should be set to read-only (not done in this example) if (!nfc_f.write16(lite_s::MC, mc, 16) || !nfc_f.read16(rbuf, lite_s::MC)) { M5_LOGE("Failed to write/read MC"); return false; } + /* if (memcmp(mc, rbuf, 16) != 0) { M5_LOGE("Failed to verify MC"); return false; } + */ M5.Log.printf(" Permission settings OK\n"); @@ -323,40 +431,6 @@ bool first_issuance_procedue_lite_s(const PICC& picc, const uint8_t master_key[2 return false; } -void issuance_write_example() -{ - // nfc_f.dump(lite_s::MC); - - const auto& picc = nfc_f.activatedPICC(); - - if (internal_auth() && external_auth()) { - M5.Log.printf("Already first_issuance_procedue_lite_s\n"); - return; - } - - // First issuance procedue - if (!first_issuance_procedue_lite_s(picc, example_master_key, example_ckv)) { - lcd.fillScreen(TFT_RED); - M5_LOGE("Failed to first_issuance_procedue"); - return; - } - -#if 1 - // Write with MAC - if (!internal_auth() || !external_auth()) { - M5_LOGE("Failed to auth"); - return; - } - constexpr char txt[16] = "writeWithMAC"; - if (!nfc_f.writeWithMAC16(0, (const uint8_t*)txt, sizeof(txt))) { - M5_LOGE("Failed to writeWithMAC"); - return; - } -#endif - - lcd.fillScreen(0); -} - } // namespace void setup() @@ -430,8 +504,8 @@ void loop() M5.update(); Units.update(); - bool clicked = M5.BtnA.wasClicked(); // For read - bool held = M5.BtnA.wasHold(); // For write + bool clicked = M5.BtnA.wasClicked(); // For access + bool held = M5.BtnA.wasHold(); // For first issuance if (clicked || held) { lcd.fillRect(0, lcd.fontHeight(), lcd.width(), lcd.height() - lcd.fontHeight()); @@ -442,9 +516,11 @@ void loop() if (picc.type == Type::FeliCaLiteS) { if (nfc_f.activate(picc)) { if (clicked) { - read_example(); + access_example(); } else { - issuance_write_example(); + if (!first_issuance_procedue_lite_s(picc, example_master_key, example_ckv)) { + M5_LOGE("Failed to first_issuance_procedue_lite_s"); + } } nfc_f.deactivate(); } diff --git a/src/nfc/layer/ndef_layer.cpp b/src/nfc/layer/ndef_layer.cpp index 312ed7e..04d72ba 100644 --- a/src/nfc/layer/ndef_layer.cpp +++ b/src/nfc/layer/ndef_layer.cpp @@ -442,9 +442,7 @@ bool NDEFLayer::write_type3(const m5::nfc::ndef::TLV& tlv) } // Read AB - if (!readAttributeBlock(ab)) { - return false; - } + (void)readAttributeBlock(ab); uint16_t first_block = _interface.first_user_block(); uint16_t last_block = _interface.last_user_block(); diff --git a/src/nfc/layer/nfc_layer_f.cpp b/src/nfc/layer/nfc_layer_f.cpp index 22e5825..a2e61b8 100644 --- a/src/nfc/layer/nfc_layer_f.cpp +++ b/src/nfc/layer/nfc_layer_f.cpp @@ -36,6 +36,17 @@ inline bool exists_picc(const std::vector& v, const PICC& picc) [&picc](const PICC& p) { return p.idm == picc.idm && p.pmm == picc.pmm; }) != v.end(); } +uint32_t get_block_list_size(const block_t* block_list, const uint8_t block_num) +{ + uint32_t sz{}; + if (block_list && block_num) { + for (uint_fast16_t i = 0; i < block_num; ++i) { + sz += 2 + block_list[i].is_3byte(); + } + } + return sz; +} + void print_block(const uint8_t buf[16], const int16_t block) { char tmp[64 + 1] = " "; @@ -70,7 +81,35 @@ namespace nfc { bool NFCLayerF::polling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, const m5::nfc::f::TimeSlot time_slot) { - return _impl->polling(picc, system_code, request_code, time_slot); + picc = {}; + + uint8_t packet[] = {m5::stl::to_underlying(CommandCode::Polling), (uint8_t)(system_code >> 8), + (uint8_t)(system_code & 0xFF), m5::stl::to_underlying(request_code), + m5::stl::to_underlying(time_slot)}; + + uint32_t timeout_ms = TIMEOUT_POLLING * TIMEOUT_POLLING_PICC * timeslot_to_slot(time_slot); + + uint8_t rbuf[18 + ((request_code != RequestCode::None) ? 2 : 0)]{}; + uint16_t rx_len = sizeof(rbuf); + if (!_impl->transceive(rbuf, rx_len, packet, sizeof(packet), timeout_ms) // + || rx_len < sizeof(rbuf) || rbuf[1] != m5::stl::to_underlying(ResponseCode::Polling)) { + if (rx_len) { + M5_LIB_LOGD("Failed to Polling %u %u %u", rx_len, rbuf[0], rbuf[1]); + } + return false; + } + + if (rx_len >= 18 && rbuf[0] >= 18) { + memcpy(picc.idm, rbuf + 2, sizeof(picc.idm)); + memcpy(picc.pmm, rbuf + 10, sizeof(picc.pmm)); + picc.request_code = request_code; + if (rbuf[0] >= 20) { + picc.request_data = ((uint16_t)rbuf[18]) << 8; + picc.request_data |= (uint16_t)rbuf[19]; + } + return true; + } + return false; } bool NFCLayerF::detect(m5::nfc::f::PICC& picc, const uint32_t timeout_ms) @@ -198,8 +237,12 @@ bool NFCLayerF::detect(std::vector& piccs, const uint16_t* pri // Lite or LiteS? if (format & format_lite) { uint8_t rbuf[16]{}; - type = read_16(rbuf, lite_s::CRC_CHECK, false) ? Type::FeliCaLiteS : Type::FeliCaLite; - if (!read_16(rbuf, lite::ID, false)) { // ID(Get DFC format) + uint16_t rx_len = sizeof(rbuf); + type = read_without_encryption_impl(rbuf, rx_len, &lite_s::CRC_CHECK, 1, &service_random_read, 1, picc1) + ? Type::FeliCaLiteS + : Type::FeliCaLite; + rx_len = sizeof(rbuf); + if (!read_without_encryption_impl(rbuf, rx_len, &lite::ID, 1, &service_random_read, 1, picc1)) { continue; } picc1.dfc_format = ((uint16_t)rbuf[8] << 8) | rbuf[9]; @@ -221,7 +264,7 @@ bool NFCLayerF::detect(std::vector& piccs, const uint16_t* pri standard::Mode mode{}; PICC tmp = picc1; tmp.type = Type::FeliCaStandard; - if (!_impl->requestResponse(mode, tmp)) { + if (!request_response_impl(tmp, mode)) { M5_LIB_LOGD("Failed to read mode"); continue; } @@ -265,56 +308,188 @@ bool NFCLayerF::requestService(uint16_t& key_version, const uint16_t node_code) return requestService(&key_version, &node_code, 1); } -bool NFCLayerF::requestService(uint16_t key_version[], const uint16_t* node_code, const uint8_t node_size) +bool NFCLayerF::requestService(uint16_t key_version[], const uint16_t* node_code, const uint8_t node_num) { - return _activePICC.valid() && _impl->requestService(key_version, _activePICC, node_code, node_size); + if (!_activePICC.valid() || !key_version || !node_code || !node_num || _activePICC.type != Type::FeliCaStandard) { + return false; + } + + std::vector packet{}; + uint32_t timeout_ms = 10; // TODO + + packet.resize(1 + 8 + 1 + (2 * node_num)); + + packet[0] = m5::stl::to_underlying(CommandCode::RequestService); + memcpy(packet.data() + 1, _activePICC.idm, 8); + packet[9] = node_num; + uint8_t* p = packet.data() + 10; + for (uint_fast8_t i = 0; i < node_num; ++i) { + *p++ = node_code[i] & 0xFF; + *p++ = node_code[i] >> 8; + } + + uint8_t rbuf[packet.size() + 1 /*LEN*/]{}; + uint16_t rx_len = sizeof(rbuf); + + // m5::utility::log::dump(packet.data(), packet.size(), false); + + if (!_impl->transceive(rbuf, rx_len, packet.data(), packet.size(), timeout_ms) // + || rx_len < sizeof(rbuf) || rbuf[1] != m5::stl::to_underlying(ResponseCode::RequestService)) { + M5_LIB_LOGE("Failed to RequestService %u", rx_len); + return false; + } + + // m5::utility::log::dump(rbuf, rx_len, false); + + for (uint_fast8_t i = 0; i < rbuf[10]; ++i) { + key_version[i] = ((uint16_t)rbuf[12 + i * 2] << 8) | rbuf[11 + i * 2]; + } + return true; } -bool NFCLayerF::requestResponse(m5::nfc::f::standard::Mode& mode) +bool NFCLayerF::request_response_impl(const m5::nfc::f::PICC& picc, m5::nfc::f::standard::Mode& mode) { - return _activePICC.valid() && _impl->requestResponse(mode, _activePICC); + mode = standard::Mode::Mode0; + + if (picc.type != Type::FeliCaStandard) { + return false; + } + + std::vector packet{}; + uint32_t timeout_ms = 10; // TODO + + packet.resize(1 + 8); + + packet[0] = m5::stl::to_underlying(CommandCode::RequestResponse); + memcpy(packet.data() + 1, picc.idm, 8); + + uint8_t rbuf[packet.size() + 1 /*LEN*/ + 1]{}; + uint16_t rx_len = sizeof(rbuf); + + // m5::utility::log::dump(packet.data(), packet.size(), false); + + if (!_impl->transceive(rbuf, rx_len, packet.data(), packet.size(), timeout_ms) // + || rx_len < sizeof(rbuf) || rbuf[1] != m5::stl::to_underlying(ResponseCode::RequestResponse)) { + M5_LIB_LOGE("Failed to RequestResponse %u", rx_len); + return false; + } + + // m5::utility::log::dump(rbuf, rx_len, false); + + mode = static_cast(rbuf[10]); + return true; } bool NFCLayerF::requestSystemCode(uint16_t code_list[255], uint8_t& code_num) { - return _activePICC.valid() && _impl->requestSystemCode(code_list, code_num, _activePICC); -} + if (code_list) { + memset(code_list, 0x00, 2 * 255); + } -bool NFCLayerF::read16(uint8_t rx[16], const m5::nfc::f::block_t block, const uint16_t service_code) -{ - return read16(rx, &block, 1, &service_code, 1); -} - -bool NFCLayerF::read16(uint8_t rx[16], const m5::nfc::f::block_t* block, const uint8_t block_num, - const uint16_t* service_code, const uint8_t service_num) -{ - uint16_t rx_len{16}; - return rx && block && block_num && service_code && service_num && _activePICC.valid() && - _impl->readWithoutEncryption(rx, rx_len, _activePICC, service_code, service_num, block, block_num) && - rx_len == 16; -} - -bool NFCLayerF::read(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::block_t* block, const uint8_t block_num) -{ - if (!_activePICC.valid() || !rx || !rx_len || !block || !block_num || block_num > _activePICC.maximumReadBlocks()) { + if (!code_list || !_activePICC.valid() || _activePICC.type != Type::FeliCaStandard) { return false; } - uint16_t sc{service_random_read}; - return _impl->readWithoutEncryption(rx, rx_len, _activePICC, &sc, 1, block, block_num) || - (rx_len != 16 * block_num); + std::vector packet{}; + uint32_t timeout_ms = 10; // TODO + + packet.resize(1 + 8); + + packet[0] = m5::stl::to_underlying(CommandCode::RequestSystemCode); + memcpy(packet.data() + 1, _activePICC.idm, 8); + + // m5::utility::log::dump(packet.data(), packet.size(), false); + + uint8_t rbuf[1 + 1 + 8 + 1 + 2 * 255]{}; + uint16_t rx_len = sizeof(rbuf); + + if (!_impl->transceive(rbuf, rx_len, packet.data(), packet.size(), timeout_ms) // + || rx_len < 11 || rbuf[1] != m5::stl::to_underlying(ResponseCode::RequestSystemCode)) { + M5_LIB_LOGE("Failed to RequestResponse %u", rx_len); + return false; + } + + // m5::utility::log::dump(rbuf, rx_len, false); + + code_num = rbuf[10]; + const auto* p = rbuf + 11; + for (uint_fast16_t i = 0; i < code_num; ++i) { + code_list[i] = p[1] | ((uint16_t)p[0] << 8); + p += 2; + } + return true; +} + +bool NFCLayerF::read16(uint8_t rx[16], const m5::nfc::f::block_t* block, const uint8_t block_num, + const uint16_t service_code) +{ + uint16_t rx_len{16}; + return _activePICC.valid() && + read_without_encryption_impl(rx, rx_len, block, block_num, &service_code, 1, _activePICC) && rx_len == 16; +} + +bool NFCLayerF::read_without_encryption_impl(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::block_t* block_list, + const uint8_t block_num, const uint16_t* service_code, + const uint8_t service_num, const PICC& picc) +{ + auto rx_org_len = rx_len; + rx_len = 0; + + if (!rx || !rx_org_len || !block_list || !block_num || !service_code || !service_num || service_num > 16) { + return false; + } + + std::vector packet{}; + const uint32_t block_size = get_block_list_size(block_list, block_num); + uint32_t timeout_ms = 50; // TODO + + packet.resize(1 + 8 + 1 + (2 * service_num) + 1 + block_size); + + packet[0] = m5::stl::to_underlying(CommandCode::ReadWithoutEncryption); + memcpy(packet.data() + 1, picc.idm, 8); + packet[9] = service_num; + uint8_t* p = packet.data() + 10; + for (uint_fast8_t i = 0; i < service_num; ++i) { + *p++ = service_code[i] & 0xFF; + *p++ = service_code[i] >> 8; + } + *p++ = block_num; + for (uint_fast8_t i = 0; i < block_num; ++i) { + block_t ble = block_list[i]; + p += ble.store(p); + } + + // m5::utility::log::dump(packet.data(), packet.size(), false); + + uint8_t rbuf[1 + 1 + 8 + 1 + 1 + 1 + 16 * block_num]{}; + uint16_t actual = sizeof(rbuf); + if (!_impl->transceive(rbuf, actual, packet.data(), packet.size(), timeout_ms) || actual < 12 || (rbuf[0] < 11) || + rbuf[1] != m5::stl::to_underlying(ResponseCode::ReadWithoutEncryption) || // + (rbuf[10] /*status 1*/ != 0x00) || (rbuf[11] /*status 2*/ != 0x00)) { + M5_LIB_LOGD("Failed to read (%02X, %u) a:%u r[0]:%u %02X%02X", block_list[0].block(), rx_org_len, actual, + rbuf[0], rbuf[10], rbuf[11]); + return false; + } + // const uint8_t blocks = rbuf[11]; + rx_len = std::min(actual - 13, rx_org_len); + memcpy(rx, rbuf + 13, rx_len); + return true; } bool NFCLayerF::read(uint8_t* rx, uint16_t& rx_len, const block_t sblock) { - auto rx_len_org = rx_len; + auto rx_org_len = rx_len; rx_len = 0; + if (!rx || !rx_org_len) { + return false; + } + uint16_t sc{service_random_read}; uint16_t start = sblock.block(); - uint16_t blocks = rx_len_org >> 4; + uint16_t blocks = rx_org_len >> 4; uint16_t last = std::min(_activePICC.lastUserBlock(), start + blocks - 1); - if (!_activePICC.valid() || !rx || !rx_len_org || !_activePICC.isUserBlock(sblock) || + if (!_activePICC.valid() || !rx || !rx_org_len || !_activePICC.isUserBlock(sblock) || !_activePICC.isUserBlock(last)) { return false; } @@ -335,7 +510,7 @@ bool NFCLayerF::read(uint8_t* rx, uint16_t& rx_len, const block_t sblock) } // M5_LIB_LOGE("rx_len:%u block_list_num:%u", actual, num); - if (!_impl->readWithoutEncryption(rbuf, actual, _activePICC, &sc, 1, block_list, num) || actual != 16 * num) { + if (!read(rbuf, actual, block_list, num, &sc, 1) || actual != 16 * num) { return false; } memcpy(out, rbuf, actual); @@ -364,8 +539,9 @@ bool NFCLayerF::readWithMAC16(uint8_t rx[16], const m5::nfc::f::block_t block) uint8_t rbuf[16 * 2]{}; uint16_t rx_len = sizeof(rbuf); - if (!read(rbuf, rx_len, block_list, 2) || rx_len < sizeof(rbuf)) { - M5_LIB_LOGE("Failed to read"); + if (!read_without_encryption_impl(rbuf, rx_len, block_list, 2, &service_random_read, 1, _activePICC) || + rx_len < sizeof(rbuf)) { + M5_LIB_LOGD("Failed to read %u", rx_len); return false; } @@ -411,10 +587,9 @@ bool NFCLayerF::readWithMAC16(uint8_t rx[16], const m5::nfc::f::block_t block) bool NFCLayerF::write16(const m5::nfc::f::block_t block, const uint8_t tx[16], const uint16_t tx_len) { if (_activePICC.valid() && tx && tx_len) { - uint16_t sc{service_random_read_write}; uint8_t buf[16]{}; memcpy(buf, tx, std::min(16u, tx_len)); - return _impl->writeWithoutEncryption(_activePICC, &sc, 1, &block, 1, buf, 16); + return write_without_encryption_impl(_activePICC, &block, 1, &service_random_read_write, 1, buf, 16); } return false; } @@ -424,6 +599,7 @@ bool NFCLayerF::write(const m5::nfc::f::block_t sblock, const uint8_t* tx, const uint16_t start = sblock.block(); uint16_t blocks = (tx_len + 15) >> 4; uint16_t last = std::min(_activePICC.lastUserBlock(), start + blocks - 1); + if (!_activePICC.valid() || !tx || !tx_len || !_activePICC.isUserBlock(sblock) || !_activePICC.isUserBlock(last)) { return false; } @@ -440,6 +616,50 @@ bool NFCLayerF::write(const m5::nfc::f::block_t sblock, const uint8_t* tx, const return true; } +bool NFCLayerF::write_without_encryption_impl(const m5::nfc::f::PICC& picc, const m5::nfc::f::block_t* block_list, + const uint8_t block_num, const uint16_t* service_code, + const uint8_t service_num, const uint8_t* tx, const uint16_t tx_len) +{ + if (!tx || !tx_len || !block_list || !block_num || !service_code || !service_num || service_num > 16) { + return false; + } + + std::vector packet{}; + const uint32_t block_size = get_block_list_size(block_list, block_num); + uint32_t timeout_ms = 10; // TODO + + packet.resize(1 + 8 + 1 + (2 * service_num) + 1 + block_size + tx_len); + + packet[0] = m5::stl::to_underlying(CommandCode::WriteWithoutEncryption); + memcpy(packet.data() + 1, picc.idm, sizeof(picc.idm)); + packet[9] = service_num; + uint8_t* p = packet.data() + 10; + for (uint_fast8_t i = 0; i < service_num; ++i) { + *p++ = service_code[i] & 0xFF; + *p++ = service_code[i] >> 8; + } + *p++ = block_num; + for (uint_fast8_t i = 0; i < block_num; ++i) { + block_t ble = block_list[i]; + p += ble.store(p); + } + memcpy(p, tx, tx_len); + + // m5::utility::log::dump(packet.data(), packet.size(), false); + + uint8_t rbuf[1 + 1 + 8 + 1 + 1]{}; + uint16_t actual = sizeof(rbuf); + if (!_impl->transceive(rbuf, actual, packet.data(), packet.size(), timeout_ms) || actual < 12 || (rbuf[0] < 11) || + rbuf[1] != m5::stl::to_underlying(ResponseCode::WriteWithoutEncryption) || // + (rbuf[10] /*status 1*/ != 0x00) || (rbuf[11] /*status 2*/ != 0x00)) { + // m5::utility::log::dump(rbuf, actual, false); + M5_LIB_LOGD("Failed to write(%02X, %u) %u %u %02X %02X", block_list[0].block(), tx_len, actual, rbuf[0], + rbuf[10], rbuf[11]); + return false; + } + return true; +} + bool NFCLayerF::writeWithMAC16(const m5::nfc::f::block_t block, const uint8_t tx[16], const uint16_t tx_len) { if (!_authenticated) { @@ -491,9 +711,14 @@ bool NFCLayerF::writeWithMAC16(const m5::nfc::f::block_t block, const uint8_t tx std::memcpy(wbuf, tx2, 16); std::memcpy(wbuf + 16, mac_host, 8); std::memcpy(wbuf + 24, wcnt, 4); - block_t block_list[2] = {block, mac_block}; + block_t block_list[2] = {block, mac_block}; // 2nd block must be MAC_A + return write_without_encryption_impl(_activePICC, block_list, 2, &service_random_read_write, 1, wbuf, sizeof(wbuf)); +} - return write_32(block_list, wbuf); +bool NFCLayerF::clearSPAD() +{ + const uint8_t w[16 * 14]{}; + return write(0, w, sizeof(w)); } bool NFCLayerF::internalAuthenticate(const uint8_t ck[16], const uint16_t ckv, const uint8_t rc[16]) @@ -537,7 +762,7 @@ bool NFCLayerF::ndefRead(m5::nfc::ndef::TLV& msg) bool NFCLayerF::ndefWrite(const m5::nfc::ndef::TLV& msg) { std::vector tlvs = {msg}; - return msg.isMessageTLV() && _activePICC.valid() && _ndef.write(_activePICC.nfcForumTagType(), tlvs); + return msg.isMessageTLV() && _activePICC.valid() && _ndef.write(_activePICC.nfcForumTagType(), tlvs, false); } bool NFCLayerF::writeSupportNDEF(const bool enabled) @@ -572,10 +797,8 @@ bool NFCLayerF::dump() return dump_felica_lite(); case Type::FeliCaLiteS: return dump_felica_lite_s(); - case Type::FeliCaStandard: - case Type::FeliCaPlug: - M5_LIB_LOGE("Not yet"); - break; + // case Type::FeliCaStandard: + // case Type::FeliCaPlug: default: M5_LIB_LOGE("Not supported %X", _activePICC.type); break; @@ -589,24 +812,6 @@ bool NFCLayerF::dump(const block_t block) } // -bool NFCLayerF::read_16(uint8_t rx[16], const block_t block, const bool check_valid) -{ - uint16_t rx_len{16}; - uint16_t sc{service_random_read}; - return rx && (check_valid ? _activePICC.valid() : true) && - _impl->readWithoutEncryption(rx, rx_len, _activePICC, &sc, 1, &block, 1) && rx_len == 16; -} - -bool NFCLayerF::write_32(const m5::nfc::f::block_t block[2], const uint8_t tx[32]) -{ - // 2nd block must be MAC_A - uint16_t sc{service_random_read_write}; - if (block && tx && block[1].block() == lite_s::MAC_A.block()) { - return _impl->writeWithoutEncryption(_activePICC, &sc, 1, block, 2, tx, 32); - } - return false; -} - bool NFCLayerF::internal_authenticate_lite_s(const uint8_t ck[16], const uint16_t ckv, const uint8_t rc[16], const bool include_wcnt) { @@ -635,7 +840,7 @@ bool NFCLayerF::internal_authenticate_lite_s(const uint8_t ck[16], const uint16_ uint8_t rbuf[16 * 4]{}; uint16_t rx_len = include_wcnt ? (4 * 16) : (3 * 16); auto needs = rx_len; - if (!read(rbuf, rx_len, block_list, include_wcnt ? 4 : 3) || rx_len != needs) { + if (!read(rbuf, rx_len, block_list, include_wcnt ? 4 : 3, &service_random_read, 1) || rx_len != needs) { M5_LIB_LOGE("Failed to read blocks %u/%u", rx_len, needs); return false; } @@ -732,8 +937,8 @@ bool NFCLayerF::external_authenticate_lite_s(const uint8_t ck[16], const uint16_ std::memcpy(tx + 16, mac_w, 8); std::memcpy(tx + 24, wcnt, 4); - block_t block_list[2] = {lite_s::STATE, lite_s::MAC_A}; - if (!write_32(block_list, tx)) { + block_t block_list[2] = {lite_s::STATE, lite_s::MAC_A}; // 2nd block must be MAC_A + if (!write_without_encryption_impl(_activePICC, block_list, 2, &service_random_read_write, 1, tx, sizeof(tx))) { M5_LIB_LOGE("Failed to write_32"); return false; } @@ -797,6 +1002,7 @@ bool NFCLayerF::read(uint8_t* rx, uint16_t& rx_len, const uint8_t saddr) if (_activePICC.checkFormat(format_ndef)) { return read(rx, rx_len, block_t(saddr)); } + M5_LIB_LOGW("PICC Not sopport NDEF"); rx_len = 0; return false; } diff --git a/src/nfc/layer/nfc_layer_f.hpp b/src/nfc/layer/nfc_layer_f.hpp index 5d2e084..6f2e7d4 100644 --- a/src/nfc/layer/nfc_layer_f.hpp +++ b/src/nfc/layer/nfc_layer_f.hpp @@ -176,7 +176,10 @@ public: @param[out] mode Mode if detected @warning FeliCa Standard only */ - bool requestResponse(m5::nfc::f::standard::Mode& mode); + inline bool requestResponse(m5::nfc::f::standard::Mode& mode) + { + return _activePICC.valid() && request_response_impl(_activePICC, mode); + } /*! @brief Request system code @@ -204,16 +207,6 @@ public: ///@note For activated PICC ///@name Read/Write without encryption - /*! - @brief Read the 1 block - @param[out] rx Output buffer - @param block Target block - @return True if successful - */ - inline bool read16(uint8_t rx[16], const m5::nfc::f::block_t block) - { - return read_16(rx, block, true); - } /*! @brief Read the 1 block with service code @param[out] rx Output buffer @@ -221,18 +214,22 @@ public: @param service_code Service code @return True if successful */ - bool read16(uint8_t rx[16], const m5::nfc::f::block_t block, const uint16_t service_code); + inline bool read16(uint8_t rx[16], const m5::nfc::f::block_t block, + const uint16_t service_code = m5::nfc::f::service_random_read) + { + return read16(rx, &block, 1, service_code); + } /*! @brief Read the 1 block with service codes @param[out] rx Output buffer @param block Target block array @param block_num Number of block + @param service_code Service code @return True if successful - @param service_code Service code array - @param service_num Number of service code + @param service_code Service code */ - bool read16(uint8_t rx[16], const m5::nfc::f::block_t* block, const uint8_t block_num, const uint16_t* service_code, - const uint8_t service_num); + bool read16(uint8_t rx[16], const m5::nfc::f::block_t* block, const uint8_t block_num, + const uint16_t service_code = m5::nfc::f::service_random_read); /*! @brief Read any bytes from user area @@ -246,15 +243,22 @@ public: bool read(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::block_t sblock); /*! - @breif Read the specified block list + @breif Read the specified block list and service codes @param[out] rx Buffer (At least 16 * block_num) @param[in/out] rx_len in:buffer size, out:actual read size @param block Target block array @param block_num Number of block + @param service_code Service code array + @param service_num Number of service code @return True if successful @warning rx in 16-byte units */ - bool read(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::block_t* block, const uint8_t block_num); + inline bool read(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::block_t* block_list, const uint8_t block_num, + const uint16_t* service_code, const uint8_t service_num) + { + return _activePICC.valid() && + read_without_encryption_impl(rx, rx_len, block_list, block_num, service_code, service_num, _activePICC); + } /*! @brief Write the 1 block @@ -275,6 +279,14 @@ public: @return True if successful */ bool write(const m5::nfc::f::block_t sblock, const uint8_t* tx, const uint16_t tx_len); + + /*! + @brief Clear SPAD_0 - 13 + @return True if successful + @pre Each area can be written to without authentication + */ + bool clearSPAD(); + ///@} ///@note For activated PICC @@ -389,8 +401,13 @@ protected: return _activePICC.maximumWriteBlocks(); } - bool read_16(uint8_t rx[16], const m5::nfc::f::block_t block, const bool check_picc_valid); - bool write_32(const m5::nfc::f::block_t block[2], const uint8_t tx[32]); // For write with MAC + bool request_response_impl(const m5::nfc::f::PICC& picc, m5::nfc::f::standard::Mode& mode); + bool read_without_encryption_impl(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::block_t* block_list, + const uint8_t block_num, const uint16_t* service_code, const uint8_t service_num, + const m5::nfc::f::PICC& picc); + bool write_without_encryption_impl(const m5::nfc::f::PICC& picc, const m5::nfc::f::block_t* block_list, + const uint8_t block_num, const uint16_t* service_code, const uint8_t service_num, + const uint8_t* tx, const uint16_t tx_len); bool dump_felica_lite(); bool dump_felica_lite_s(); @@ -422,20 +439,8 @@ private: struct NFCLayerF::Adapter { virtual ~Adapter() = default; - virtual bool polling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, - const m5::nfc::f::TimeSlot time_slot) = 0; - - virtual bool requestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, const uint16_t* node_code, - const uint8_t node_num) = 0; - virtual bool requestResponse(m5::nfc::f::standard::Mode& mode, const m5::nfc::f::PICC& picc) = 0; - virtual bool requestSystemCode(uint16_t code_list[255], uint8_t& code_num, const m5::nfc::f::PICC& picc) = 0; - - virtual bool readWithoutEncryption(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc, - const uint16_t* service_code, const uint8_t service_num, - const m5::nfc::f::block_t* block_list, const uint8_t block_num) = 0; - virtual bool writeWithoutEncryption(const m5::nfc::f::PICC& picc, const uint16_t* service_code, - const uint8_t service_num, const m5::nfc::f::block_t* block_list, - const uint8_t block_num, const uint8_t* tx, const uint16_t tx_len) = 0; + virtual bool transceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, + const uint32_t timeout_ms) = 0; }; ///@endcond diff --git a/src/nfc/layer/unit/nfc_layer_f_ST25R3916.cpp b/src/nfc/layer/unit/nfc_layer_f_ST25R3916.cpp index 04f12ad..61e96b2 100644 --- a/src/nfc/layer/unit/nfc_layer_f_ST25R3916.cpp +++ b/src/nfc/layer/unit/nfc_layer_f_ST25R3916.cpp @@ -25,58 +25,16 @@ struct AdapterST25R3916ForF final : NFCLayerF::Adapter { { } - virtual bool polling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, - const m5::nfc::f::TimeSlot time_slot) override; - - virtual bool requestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, const uint16_t* node_code, - const uint8_t node_num) override; - virtual bool requestResponse(m5::nfc::f::standard::Mode& mode, const m5::nfc::f::PICC& picc) override; - virtual bool requestSystemCode(uint16_t code_list[255], uint8_t& code_num, const m5::nfc::f::PICC& picc) override; - - virtual bool readWithoutEncryption(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc, - const uint16_t* service_code, const uint8_t service_num, - const block_t* block_list, const uint8_t block_num) override; - virtual bool writeWithoutEncryption(const m5::nfc::f::PICC& picc, const uint16_t* service_code, - const uint8_t service_num, const m5::nfc::f::block_t* block_list, - const uint8_t block_num, const uint8_t* tx, const uint16_t tx_len) override; + virtual bool transceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, + const uint32_t timeout_ms) override; UnitST25R3916& _u; }; -bool AdapterST25R3916ForF::polling(m5::nfc::f::PICC& picc, const uint16_t system_code, - const m5::nfc::f::RequestCode request_code, const m5::nfc::f::TimeSlot time_slot) +bool AdapterST25R3916ForF::transceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, + const uint32_t timeout_ms) { - return _u.nfcfPolling(picc, system_code, request_code, time_slot); -} - -bool AdapterST25R3916ForF::requestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, - const uint16_t* node_code, const uint8_t node_num) -{ - return _u.nfcfRequestService(key_version, picc, node_code, node_num); -} - -bool AdapterST25R3916ForF::requestResponse(m5::nfc::f::standard::Mode& mode, const m5::nfc::f::PICC& picc) -{ - return _u.nfcfRequestResponse(mode, picc); -} - -bool AdapterST25R3916ForF::requestSystemCode(uint16_t code_list[255], uint8_t& code_num, const m5::nfc::f::PICC& picc) -{ - return _u.nfcfRequestSystemCode(code_list, code_num, picc); -} - -bool AdapterST25R3916ForF::readWithoutEncryption(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc, - const uint16_t* service_code, const uint8_t service_num, - const block_t* block_list, const uint8_t block_size) -{ - return _u.nfcfReadWithoutEncryption(rx, rx_len, picc, service_code, service_num, block_list, block_size); -} - -bool AdapterST25R3916ForF::writeWithoutEncryption(const m5::nfc::f::PICC& picc, const uint16_t* service_code, - const uint8_t service_num, const m5::nfc::f::block_t* block_list, - const uint8_t block_num, const uint8_t* tx, const uint16_t tx_len) -{ - return _u.nfcfWriteWithoutEncryption(picc, service_code, service_num, block_list, block_num, tx, tx_len); + return _u.nfcfTransceive(rx, rx_len, tx, tx_len, timeout_ms); } // diff --git a/src/unit/unit_ST25R3916.hpp b/src/unit/unit_ST25R3916.hpp index caffc5f..66ee20d 100644 --- a/src/unit/unit_ST25R3916.hpp +++ b/src/unit/unit_ST25R3916.hpp @@ -1911,9 +1911,32 @@ public: // ---------------------------------------------------------------------------------------------- ///@name NFC-B ///@{ + /*! + @brief Transceive with NFC-B PICC + @param[out] rx Receive buffer + @param[in/out] rx_len in:Size of receive buffer out:actual read size + @param tx Send buffer + @param tx_len Size of send buffer + @param timeout_ms Timeout(ms) + @return True if successful + */ bool nfcbTransceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms); + /*! + @param Transmit to NFC-B PICC + @param tx Send buffer + @param tx_len Size of send buffer + @param timeout_ms Timeout(ms) + @return True if successful + */ bool nfcbTransmit(const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms); + /*! + @param Receive from NFC-B PICC + @param[out] rx Receive buffer + @param[in/out] rx_len in:Size of receive buffer out:actual read size + @param timeout_ms Timeout(ms) + @return True if successful + */ bool nfcbReceive(uint8_t* rx, uint16_t& rx_len, const uint32_t timeout_ms); ///@} @@ -1921,7 +1944,7 @@ public: ///@name NFC-F ///@{ /*! - @brief Transceive + @brief Transceive with NFC-F PICC @param[out] rx Receive buffer @param[in/out] rx_len in:Size of receive buffer out:actual read size @param tx Send buffer @@ -1931,84 +1954,23 @@ public: */ bool nfcfTransceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms); + /*! + @param Transmit to NFC-F PICC + @param tx Send buffer + @param tx_len Size of send buffer + @param timeout_ms Timeout(ms) + @return True if successful + */ bool nfcfTransmit(const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms); + /*! + @param Receive from NFC-F PICC + @param[out] rx Receive buffer + @param[in/out] rx_len in:Size of receive buffer out:actual read size + @param timeout_ms Timeout(ms) + @return True if successful + */ bool nfcfReceive(uint8_t* rx, uint16_t& rx_len, const uint32_t timeout_ms); - /*! - @brief Polling - @param[out] PICC detected PICC - @param system_code System code - @param request_code Request code - @param time_slot Maximum number of slots that can be responded - @return True if successful - @note SENSF_REQ - */ - bool nfcfPolling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, - const m5::nfc::f::TimeSlot time_slot); - - /*! - @brief Requset service - @param[out] ky_version Node key version array (at leaset node_size) - @param picc PICC - @param node_code Node code array - @param node_num Number of elements in the node_code array - @return True if successful - @warning FeliCa Standard only - */ - bool nfcfRequestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, const uint16_t* node_code, - const uint8_t node_num); - - /*! - @brief Request response - @param[out] mode Mode if detected - @param picc PICC - @return True if successful - @warning FeliCa Standard only - */ - bool nfcfRequestResponse(m5::nfc::f::standard::Mode& mode, const m5::nfc::f::PICC& picc); - - /*! - @brief Request system code - @param[out] code_list Code list array (at least 255) - @param[out] code_num Number of code_list - @param picc PICC - @return True if successful - @warning FeliCa Standard only - */ - bool nfcfRequestSystemCode(uint16_t code_list[255], uint8_t& code_num, const m5::nfc::f::PICC& picc); - - /*! - @brief Read the area that does not require authentication - @param[out] rx Buffer(at least 16 bytes * number of blocks) - @param[in/out] rx_len in:Bufffer size out:Actual size - @param picc PICC - @param service_code Service code array - @param service_num Number of elements in the service_code array (1-16) - @param block_list Block list - @param block_num Number of elements in the block_list array (1-8) - @return True if successful - @note The read unit is 16 bytes - @warning The maximum number of blocks read varies by type - */ - bool nfcfReadWithoutEncryption(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc, - const uint16_t* service_code, const uint8_t service_num, - const m5::nfc::f::block_t* block_list, const uint8_t block_num); - /*! - @brief Write the area that does not require authentication - @param picc PICC - @param service_code Service code array - @param service_num Number of elements in the service_code array (1-16) - @param block_list Block list - @param block_num Number of elements in the block_list array - @param tx Buffer - @param tx_len tx size - @return True if successful - @note The write unit is 16 bytes - @warning The maximum number of blocks write varies by type - */ - bool nfcfWriteWithoutEncryption(const m5::nfc::f::PICC& picc, const uint16_t* service_code, - const uint8_t service_num, const m5::nfc::f::block_t* block_list, - const uint8_t block_num, const uint8_t* tx, const uint16_t tx_len); ///@} // ---------------------------------------------------------------------------------------------- diff --git a/src/unit/unit_ST25R3916_nfcf.cpp b/src/unit/unit_ST25R3916_nfcf.cpp index 18960b0..71784ac 100644 --- a/src/unit/unit_ST25R3916_nfcf.cpp +++ b/src/unit/unit_ST25R3916_nfcf.cpp @@ -37,17 +37,6 @@ uint8_t val_table[] = { }; */ - -uint32_t get_block_list_size(const block_t* block_list, const uint8_t block_num) -{ - uint32_t sz{}; - if (block_list && block_num) { - for (uint_fast16_t i = 0; i < block_num; ++i) { - sz += 2 + block_list[i].is_3byte(); - } - } - return sz; -} } // namespace namespace m5 { @@ -109,49 +98,7 @@ bool UnitST25R3916::configure_emulation_f() bool UnitST25R3916::nfcfTransceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms) { -#if 0 - CHECK_MODE(); - - const auto rx_len_org = rx_len; - rx_len = 0; - - if (!tx || !tx_len) { - return false; - } - - if (timeout_ms && !write_fwt_timer(timeout_ms)) { - return false; - } - if (!clear_bit_register8(REG_AUXILIARY_DEFINITION, no_crc_rx) || !clearInterrupts() || - !writeDirectCommand(CMD_CLEAR_FIFO) || !writeFIFO(tx, tx_len) || !writeNumberOfTransmittedBytes(tx_len, 0) || - !writeDirectCommand(CMD_TRANSMIT_WITH_CRC)) { - return false; - } - auto irq32 = wait_for_interrupt(I_txe32, timeout_ms) & I_txe32; - if (!irq32) { - return false; - } - // Send only - if (!rx && !rx_len) { - return true; - } - - if (rx && rx_len_org) { - if (!wait_for_FIFO(timeout_ms, rx_len_org)) { - M5_LIB_LOGD("Timeout"); - return false; - } - uint16_t actual{}; - if (readFIFO(actual, rx, rx_len_org)) { - rx_len = actual; - return true; - } - M5_LIB_LOGD("Failed to readFIFO %u/%u", actual, rx_len_org); - } - return false; -#else return nfcfTransmit(tx, tx_len, timeout_ms) && nfcfReceive(rx, rx_len, timeout_ms); -#endif } bool UnitST25R3916::nfcfTransmit(const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms) @@ -197,6 +144,7 @@ bool UnitST25R3916::nfcfReceive(uint8_t* rx, uint16_t& rx_len, const uint32_t ti return false; } +#if 0 bool UnitST25R3916::nfcfPolling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, const m5::nfc::f::TimeSlot time_slot) { @@ -286,7 +234,6 @@ bool UnitST25R3916::nfcfRequestResponse(m5::nfc::f::standard::Mode& mode, const mode = standard::Mode::Mode0; if (picc.type != Type::FeliCaStandard) { - M5_LIB_LOGE("========================"); return false; } @@ -452,6 +399,7 @@ bool UnitST25R3916::nfcfWriteWithoutEncryption(const m5::nfc::f::PICC& picc, con } return true; } +#endif } // namespace unit } // namespace m5