mirror of
https://github.com/m5stack/M5Unit-NFC.git
synced 2026-05-20 11:48:34 -07:00
Move the processing to the layer side(NFCF)
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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 <M5Unified.h>
|
||||
#include <M5UnitUnified.h>
|
||||
#include <M5UnitUnifiedNFC.h>
|
||||
#include <M5Utility.h>
|
||||
#include <vector>
|
||||
|
||||
// *************************************************************
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
+15
-1
@@ -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()
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
NOTICE: Please note that cards that have undergone the initial issuance procedure cannot be read without subsequent
|
||||
authentication.
|
||||
*******************************************************************************************************************
|
||||
*/
|
||||
*/
|
||||
#include <M5Unified.h>
|
||||
#include <M5UnitUnified.h>
|
||||
#include <M5UnitUnifiedNFC.h>
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user