881  NFC Hands-On Lab

881.1 Learning Objectives

By completing this lab, you will be able to:

  • Understand NFC Tag Structure: Learn how NFC tags store data in memory blocks
  • Parse NDEF Messages: Decode the NFC Data Exchange Format structure
  • Identify Tag Types: Recognize characteristics of different NFC tag types (Type 1-5)
  • Implement Security Checks: Validate tag authenticity and detect tampering
  • Simulate P2P Concepts: Understand peer-to-peer communication handshake patterns

881.2 Prerequisites

Before starting this lab, you should have completed:

881.3 NFC Communication Lab

Time: ~30 min | Level: Intermediate | Lab Type: Wokwi Simulation

This hands-on lab demonstrates NFC communication concepts using an ESP32-based simulation. Since actual NFC hardware requires specialized modules (like the PN532 or RC522), this lab simulates NFC tag reading, NDEF message parsing, and security concepts to help you understand the underlying principles before working with real hardware.

NoteAbout This Simulation

Real NFC communication requires specialized hardware (PN532, RC522, or smartphone NFC chip). This Wokwi simulation demonstrates the software concepts behind NFC: tag memory structure, NDEF parsing, security validation, and protocol state machines. The same code patterns apply when you transition to real NFC hardware.

881.3.1 Lab Components

Component Purpose Simulation Role
ESP32 DevKit Main controller Simulates NFC reader/writer logic
Push Buttons Tag simulation triggers Simulate different tag types being “tapped”
LEDs Status indicators Show read/write/error states
Serial Monitor Output display Shows NDEF parsing and security analysis

881.3.2 Wokwi Simulator Environment

NoteAbout Wokwi

Wokwi is a free online simulator for Arduino, ESP32, and other microcontrollers. While it does not simulate actual NFC hardware, we use it to demonstrate the software logic that processes NFC data. The code patterns you learn here directly apply to real NFC development with modules like PN532 or RC522.

Launch the simulator below and copy the provided code to explore NFC concepts interactively.

TipSimulator Tips
  • Click the + button to add components (search for “Push Button” and “LED”)
  • Use the Serial Monitor to see NFC simulation output (115200 baud)
  • Press buttons to simulate different NFC tag types being tapped
  • The code demonstrates real NDEF parsing logic used in actual NFC applications

881.3.3 Step-by-Step Instructions

881.3.3.1 Step 1: Set Up the Circuit

  1. Add 4 Push Buttons: Click + and search for “Push Button” - add 4 buttons
  2. Add 3 LEDs: Click + and search for “LED” - add Red, Green, and Blue LEDs
  3. Wire the components to ESP32:
    • Button 1 (NTAG213 sim) -> GPIO 12
    • Button 2 (NTAG216 sim) -> GPIO 13
    • Button 3 (DESFire sim) -> GPIO 14
    • Button 4 (Malicious tag sim) -> GPIO 15
    • Green LED (Success) -> GPIO 25
    • Red LED (Error/Security) -> GPIO 26
    • Blue LED (Processing) -> GPIO 27
    • All button other pins -> GND
    • All LED cathodes -> GND (with appropriate resistors)

%% fig-alt: Circuit diagram showing ESP32 connected to four push buttons for simulating different NFC tag types and three LEDs for status indication in the NFC communication lab
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#7F8C8D'}}}%%
flowchart LR
    subgraph ESP32["ESP32 DevKit"]
        G12["GPIO 12<br/>(NTAG213)"]
        G13["GPIO 13<br/>(NTAG216)"]
        G14["GPIO 14<br/>(DESFire)"]
        G15["GPIO 15<br/>(Malicious)"]
        G25["GPIO 25<br/>(Green LED)"]
        G26["GPIO 26<br/>(Red LED)"]
        G27["GPIO 27<br/>(Blue LED)"]
        GND["GND"]
    end

    subgraph Buttons["NFC Tag Simulators"]
        B1["Button 1<br/>NTAG213"]
        B2["Button 2<br/>NTAG216"]
        B3["Button 3<br/>DESFire"]
        B4["Button 4<br/>Malicious"]
    end

    subgraph LEDs["Status Indicators"]
        LEDG["Green LED<br/>Success"]
        LEDR["Red LED<br/>Security Alert"]
        LEDB["Blue LED<br/>Processing"]
    end

    G12 -.->|"Input"| B1
    G13 -.->|"Input"| B2
    G14 -.->|"Input"| B3
    G15 -.->|"Input"| B4
    G25 -.->|"Output"| LEDG
    G26 -.->|"Output"| LEDR
    G27 -.->|"Output"| LEDB

    style ESP32 fill:#2C3E50,stroke:#16A085,stroke-width:2px,color:#fff
    style Buttons fill:#16A085,stroke:#2C3E50,stroke-width:2px,color:#fff
    style LEDs fill:#E67E22,stroke:#2C3E50,stroke-width:2px,color:#fff

881.3.3.2 Step 2: Understanding the NFC Simulation Architecture

This lab simulates the key NFC concepts through software:

%% fig-alt: NFC simulation architecture showing how button presses trigger simulated tag reads which are processed through NDEF parser and security validator modules before displaying results
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#7F8C8D'}}}%%
flowchart TB
    subgraph Input["Tag Simulation Input"]
        BTN["Button Press"]
        SIM["Simulated Tag Data<br/>(Memory blocks, UID, Type)"]
    end

    subgraph Processing["NFC Processing Logic"]
        NDEF["NDEF Parser<br/>- Header decoding<br/>- Record extraction<br/>- Payload parsing"]
        SEC["Security Validator<br/>- UID verification<br/>- CMAC check<br/>- Counter validation"]
        TYPE["Tag Type Analyzer<br/>- Memory capacity<br/>- Feature detection<br/>- Compatibility check"]
    end

    subgraph Output["Results"]
        SERIAL["Serial Output<br/>Detailed analysis"]
        LED["LED Indicators<br/>Visual feedback"]
    end

    BTN --> SIM
    SIM --> NDEF
    SIM --> SEC
    SIM --> TYPE
    NDEF --> SERIAL
    SEC --> SERIAL
    TYPE --> SERIAL
    SEC --> LED

    style Input fill:#16A085,stroke:#2C3E50,stroke-width:2px,color:#fff
    style Processing fill:#2C3E50,stroke:#16A085,stroke-width:2px,color:#fff
    style Output fill:#E67E22,stroke:#2C3E50,stroke-width:2px,color:#fff

881.3.3.3 Step 3: Copy the NFC Communication Lab Code

Copy the following code into the Wokwi code editor. This comprehensive simulation demonstrates NFC tag reading, NDEF parsing, tag type identification, and security validation.

/*
 * NFC Communication Lab - ESP32 Simulation
 *
 * This code demonstrates NFC concepts without requiring actual NFC hardware:
 * - NFC tag memory structure and organization
 * - NDEF (NFC Data Exchange Format) message parsing
 * - Tag type identification (Type 1-5, NTAG, DESFire)
 * - Security validation and tamper detection
 * - Peer-to-peer communication concepts
 *
 * Hardware: ESP32 DevKit with buttons and LEDs
 * Purpose: Educational simulation of NFC protocols
 *
 * Button 1 (GPIO 12): Simulate NTAG213 tag (144 bytes, basic)
 * Button 2 (GPIO 13): Simulate NTAG216 tag (888 bytes, Smart Poster)
 * Button 3 (GPIO 14): Simulate DESFire tag (secure, encrypted)
 * Button 4 (GPIO 15): Simulate malicious/tampered tag (security demo)
 *
 * LEDs:
 * - Green (GPIO 25): Successful read/valid tag
 * - Red (GPIO 26): Security alert/invalid tag
 * - Blue (GPIO 27): Processing/reading
 */

#include <Arduino.h>

// ========== PIN DEFINITIONS ==========
#define BTN_NTAG213   12    // Simulate NTAG213 tag
#define BTN_NTAG216   13    // Simulate NTAG216 tag
#define BTN_DESFIRE   14    // Simulate DESFire secure tag
#define BTN_MALICIOUS 15    // Simulate malicious tag

#define LED_SUCCESS   25    // Green LED - successful operation
#define LED_SECURITY  26    // Red LED - security alert
#define LED_PROCESSING 27   // Blue LED - reading/processing

// ========== NFC TAG TYPE DEFINITIONS ==========
enum NFCTagType {
    TAG_TYPE_UNKNOWN = 0,
    TAG_TYPE_1,         // Topaz (Broadcom) - 96 bytes to 2KB
    TAG_TYPE_2,         // NTAG/Ultralight - 48 bytes to 2KB
    TAG_TYPE_3,         // FeliCa - variable, 212/424 Kbps
    TAG_TYPE_4,         // DESFire, ISO-DEP - 4KB to 32KB
    TAG_TYPE_5          // ISO 15693 (ICODE) - 256 bytes to 8KB
};

// ========== NDEF RECORD TYPE DEFINITIONS ==========
#define TNF_EMPTY           0x00
#define TNF_WELL_KNOWN      0x01    // NFC Forum well-known type
#define TNF_MEDIA           0x02    // RFC 2046 media type
#define TNF_ABSOLUTE_URI    0x03    // Absolute URI
#define TNF_EXTERNAL        0x04    // NFC Forum external type
#define TNF_UNKNOWN         0x05
#define TNF_UNCHANGED       0x06
#define TNF_RESERVED        0x07

// URI identifier codes (first byte of URI payload)
const char* URI_PREFIXES[] = {
    "",                     // 0x00 - no prepending
    "http://www.",          // 0x01
    "https://www.",         // 0x02
    "http://",              // 0x03
    "https://",             // 0x04
    "tel:",                 // 0x05
    "mailto:",              // 0x06
};

// ========== SIMULATED TAG DATA STRUCTURES ==========

// NTAG213 simulated memory (144 bytes user memory)
struct NTAG213_Simulated {
    uint8_t uid[7];
    uint8_t capability_container[4];
    uint8_t user_memory[144];
} ntag213_tag;

// NTAG216 simulated memory (888 bytes user memory)
struct NTAG216_Simulated {
    uint8_t uid[7];
    uint8_t capability_container[4];
    uint8_t user_memory[888];
    uint32_t counter;
} ntag216_tag;

// DESFire simulated structure
struct DESFire_Simulated {
    uint8_t uid[7];
    uint8_t sak;
    bool authenticated;
    uint32_t counter;
    uint8_t cmac[8];
} desfire_tag;

// ========== GLOBAL VARIABLES ==========
unsigned long lastButtonPress = 0;
const unsigned long DEBOUNCE_DELAY = 300;
int tagsRead = 0;
int securityAlerts = 0;

// ========== HELPER FUNCTIONS ==========

void printHex(const uint8_t* data, size_t len) {
    for (size_t i = 0; i < len; i++) {
        if (data[i] < 0x10) Serial.print("0");
        Serial.print(data[i], HEX);
        if (i < len - 1) Serial.print(" ");
    }
}

void setLEDs(bool success, bool security, bool processing) {
    digitalWrite(LED_SUCCESS, success ? HIGH : LOW);
    digitalWrite(LED_SECURITY, security ? HIGH : LOW);
    digitalWrite(LED_PROCESSING, processing ? HIGH : LOW);
}

void blinkLED(int pin, int count, int delayMs) {
    for (int i = 0; i < count; i++) {
        digitalWrite(pin, HIGH);
        delay(delayMs);
        digitalWrite(pin, LOW);
        delay(delayMs);
    }
}

void simulateCMAC(const uint8_t* data, size_t len, uint8_t* cmac) {
    for (int i = 0; i < 8; i++) {
        cmac[i] = 0;
        for (size_t j = i; j < len; j += 8) {
            cmac[i] ^= data[j];
        }
        cmac[i] ^= 0xA5;
    }
}

const char* getTNFName(uint8_t tnf) {
    switch (tnf) {
        case TNF_EMPTY: return "Empty";
        case TNF_WELL_KNOWN: return "NFC Forum Well-Known";
        case TNF_MEDIA: return "Media Type (RFC 2046)";
        case TNF_ABSOLUTE_URI: return "Absolute URI";
        case TNF_EXTERNAL: return "NFC Forum External";
        case TNF_UNKNOWN: return "Unknown";
        default: return "Reserved";
    }
}

// ========== TAG INITIALIZATION FUNCTIONS ==========

void initNTAG213() {
    ntag213_tag.uid[0] = 0x04;
    ntag213_tag.uid[1] = 0xA1;
    ntag213_tag.uid[2] = 0xB2;
    ntag213_tag.uid[3] = 0xC3;
    ntag213_tag.uid[4] = 0xD4;
    ntag213_tag.uid[5] = 0xE5;
    ntag213_tag.uid[6] = 0xF6;

    ntag213_tag.capability_container[0] = 0xE1;  // NDEF magic
    ntag213_tag.capability_container[1] = 0x10;  // Version 1.0
    ntag213_tag.capability_container[2] = 0x12;  // Size: 144 bytes
    ntag213_tag.capability_container[3] = 0x00;  // Read/write

    memset(ntag213_tag.user_memory, 0, sizeof(ntag213_tag.user_memory));

    // Write NDEF URL record
    int idx = 0;
    ntag213_tag.user_memory[idx++] = 0x03;  // NDEF TLV
    ntag213_tag.user_memory[idx++] = 24;    // Length
    ntag213_tag.user_memory[idx++] = 0xD1;  // MB=1, ME=1, SR=1, TNF=well-known
    ntag213_tag.user_memory[idx++] = 0x01;  // Type length
    ntag213_tag.user_memory[idx++] = 20;    // Payload length
    ntag213_tag.user_memory[idx++] = 'U';   // Type: URI
    ntag213_tag.user_memory[idx++] = 0x04;  // https://
    const char* url = "example.com/iot";
    memcpy(&ntag213_tag.user_memory[idx], url, strlen(url));
    idx += strlen(url);
    ntag213_tag.user_memory[idx++] = 0xFE;  // Terminator

    Serial.println("NTAG213 initialized with URL: https://example.com/iot");
}

void initNTAG216() {
    ntag216_tag.uid[0] = 0x04;
    ntag216_tag.uid[1] = 0x12;
    ntag216_tag.uid[2] = 0x34;
    ntag216_tag.uid[3] = 0x56;
    ntag216_tag.uid[4] = 0x78;
    ntag216_tag.uid[5] = 0x9A;
    ntag216_tag.uid[6] = 0xBC;

    ntag216_tag.capability_container[0] = 0xE1;
    ntag216_tag.capability_container[1] = 0x10;
    ntag216_tag.capability_container[2] = 0x6D;  // ~888 bytes
    ntag216_tag.capability_container[3] = 0x00;

    ntag216_tag.counter = 42;
    memset(ntag216_tag.user_memory, 0, sizeof(ntag216_tag.user_memory));

    // Smart Poster NDEF
    int idx = 0;
    ntag216_tag.user_memory[idx++] = 0x03;  // NDEF TLV
    ntag216_tag.user_memory[idx++] = 50;    // Length
    ntag216_tag.user_memory[idx++] = 0xD1;  // Header
    ntag216_tag.user_memory[idx++] = 0x02;  // Type length
    ntag216_tag.user_memory[idx++] = 46;    // Payload length
    ntag216_tag.user_memory[idx++] = 'S';   // Smart Poster
    ntag216_tag.user_memory[idx++] = 'p';

    // Nested URI
    ntag216_tag.user_memory[idx++] = 0x91;  // MB=1, ME=0
    ntag216_tag.user_memory[idx++] = 0x01;
    ntag216_tag.user_memory[idx++] = 20;
    ntag216_tag.user_memory[idx++] = 'U';
    ntag216_tag.user_memory[idx++] = 0x04;
    const char* sp_url = "github.com/iot-lab";
    memcpy(&ntag216_tag.user_memory[idx], sp_url, strlen(sp_url));
    idx += strlen(sp_url);

    ntag216_tag.user_memory[idx++] = 0xFE;

    Serial.println("NTAG216 initialized with Smart Poster");
}

void initDESFire() {
    desfire_tag.uid[0] = 0x04;
    desfire_tag.uid[1] = 0xDE;
    desfire_tag.uid[2] = 0x5F;
    desfire_tag.uid[3] = 0x1E;
    desfire_tag.uid[4] = 0xE0;
    desfire_tag.uid[5] = 0x01;
    desfire_tag.uid[6] = 0x23;

    desfire_tag.sak = 0x20;
    desfire_tag.authenticated = true;
    desfire_tag.counter = 157;

    simulateCMAC(desfire_tag.uid, 7, desfire_tag.cmac);

    Serial.println("DESFire EV2 initialized (secure tag simulation)");
}

// ========== TAG READING SIMULATION FUNCTIONS ==========

void simulateReadNTAG213() {
    Serial.println("\n========================================");
    Serial.println("  NFC TAG DETECTED: NTAG213");
    Serial.println("========================================\n");

    setLEDs(false, false, true);
    delay(200);

    Serial.println("--- TAG IDENTIFICATION ---");
    Serial.print("Tag Type: NFC Forum Type 2 Tag\n");
    Serial.print("IC: NTAG213 (NXP)\n");
    Serial.print("UID (7 bytes): ");
    printHex(ntag213_tag.uid, 7);
    Serial.println();

    Serial.println("\n--- MEMORY INFORMATION ---");
    Serial.println("Total memory: 180 bytes");
    Serial.println("User memory: 144 bytes");

    Serial.println("\n--- CAPABILITY CONTAINER ---");
    Serial.print("CC Bytes: ");
    printHex(ntag213_tag.capability_container, 4);
    Serial.println();
    Serial.print("NDEF Magic: 0x");
    Serial.print(ntag213_tag.capability_container[0], HEX);
    Serial.println(ntag213_tag.capability_container[0] == 0xE1 ? " (valid)" : " (invalid!)");

    Serial.println("\n--- NDEF MESSAGE ---");
    Serial.println("Record Type: URI");
    Serial.println("URI: https://example.com/iot");

    Serial.println("\n--- SECURITY ANALYSIS ---");
    Serial.println("UID Validation: PASS");
    Serial.println("Note: NTAG213 has basic security (password protection only)");

    setLEDs(true, false, false);
    tagsRead++;

    Serial.println("\n========================================\n");
}

void simulateReadNTAG216() {
    Serial.println("\n========================================");
    Serial.println("  NFC TAG DETECTED: NTAG216");
    Serial.println("========================================\n");

    setLEDs(false, false, true);
    delay(250);

    ntag216_tag.counter++;

    Serial.println("--- TAG IDENTIFICATION ---");
    Serial.print("Tag Type: NFC Forum Type 2 Tag (Extended)\n");
    Serial.print("IC: NTAG216 (NXP)\n");
    Serial.print("UID: ");
    printHex(ntag216_tag.uid, 7);
    Serial.println();

    Serial.println("\n--- MEMORY INFORMATION ---");
    Serial.println("User memory: 888 bytes");

    Serial.println("\n--- NFC COUNTER ---");
    Serial.print("Read counter: ");
    Serial.println(ntag216_tag.counter);
    Serial.println("Counter increments on each read - helps detect cloning");

    Serial.println("\n--- NDEF MESSAGE ---");
    Serial.println("Smart Poster detected:");
    Serial.println("  URI: https://github.com/iot-lab");

    Serial.println("\n--- SECURITY FEATURES ---");
    Serial.println("  + NFC counter (anti-clone)");
    Serial.println("  + Password protection available");
    Serial.println("  - No cryptographic authentication");

    setLEDs(true, false, false);
    tagsRead++;

    Serial.println("\n========================================\n");
}

void simulateReadDESFire() {
    Serial.println("\n========================================");
    Serial.println("  NFC TAG DETECTED: MIFARE DESFire EV2");
    Serial.println("========================================\n");

    setLEDs(false, false, true);
    delay(300);

    desfire_tag.counter++;
    simulateCMAC(desfire_tag.uid, 7, desfire_tag.cmac);

    Serial.println("--- TAG IDENTIFICATION ---");
    Serial.println("Tag Type: NFC Forum Type 4 Tag");
    Serial.println("IC: MIFARE DESFire EV2 (NXP)");
    Serial.print("UID: ");
    printHex(desfire_tag.uid, 7);
    Serial.println();
    Serial.print("SAK: 0x");
    Serial.print(desfire_tag.sak, HEX);
    Serial.println(" (ISO 14443-4 compliant)");

    Serial.println("\n--- CRYPTOGRAPHIC SECURITY ---");
    Serial.println("Encryption: AES-128 CBC");
    Serial.println("Authentication: 3-pass mutual authentication");
    Serial.print("Session authenticated: ");
    Serial.println(desfire_tag.authenticated ? "YES" : "NO");
    Serial.print("Transaction counter: ");
    Serial.println(desfire_tag.counter);

    Serial.print("CMAC: ");
    printHex(desfire_tag.cmac, 8);
    Serial.println();

    Serial.println("\n--- SECURITY FEATURES ---");
    Serial.println("  [+] AES-128 encryption");
    Serial.println("  [+] Cryptographic authentication");
    Serial.println("  [+] Transaction counter");
    Serial.println("  [+] CMAC message authentication");
    Serial.println("  [+] Anti-cloning protection");

    Serial.println("\n--- RECOMMENDED APPLICATIONS ---");
    Serial.println("  * Access control badges");
    Serial.println("  * Transit ticketing");
    Serial.println("  * Product authentication");

    setLEDs(true, false, false);
    tagsRead++;

    Serial.println("\n========================================\n");
}

void simulateMaliciousTag() {
    Serial.println("\n========================================");
    Serial.println("  !! SECURITY ALERT: SUSPICIOUS TAG !!");
    Serial.println("========================================\n");

    setLEDs(false, true, true);
    delay(100);

    int attackType = random(1, 5);

    switch (attackType) {
        case 1:
            Serial.println("ATTACK TYPE: Cloned Tag Detected");
            Serial.println("-----------------------------------");
            Serial.println("Indicators:");
            Serial.println("  [!] Counter value is LOWER than expected");
            Serial.println("      Expected: >= 158, Received: 42");
            Serial.println("  [!] This suggests tag was cloned");
            Serial.println("\nMitigation:");
            Serial.println("  - Reject this tag");
            Serial.println("  - Investigate security breach");
            break;

        case 2:
            Serial.println("ATTACK TYPE: Malicious URL Injection");
            Serial.println("--------------------------------------");
            Serial.println("NDEF payload contains suspicious URL:");
            Serial.println("  URL: https://secure-bank-login.tk/verify");
            Serial.println("\nRed Flags:");
            Serial.println("  [!] .tk domain (commonly used for phishing)");
            Serial.println("  [!] Contains 'login', 'bank', 'verify' keywords");
            Serial.println("\nMitigation:");
            Serial.println("  - Block URL from opening");
            Serial.println("  - Warn user about phishing attempt");
            break;

        case 3:
            Serial.println("ATTACK TYPE: CMAC Validation Failed");
            Serial.println("-------------------------------------");
            Serial.println("Cryptographic authentication failed:");
            Serial.println("  Expected CMAC: A5 B7 C9 D1 E3 F5 07 19");
            Serial.println("  Received CMAC: 00 00 00 00 00 00 00 00");
            Serial.println("\nPossible causes:");
            Serial.println("  [!] Tag may be counterfeit");
            Serial.println("  [!] Replay attack attempted");
            Serial.println("\nMitigation:");
            Serial.println("  - Reject authentication");
            Serial.println("  - Log incident for review");
            break;

        case 4:
            Serial.println("ATTACK TYPE: Invalid UID Format");
            Serial.println("---------------------------------");
            Serial.println("UID anomalies detected:");
            Serial.println("  Received UID: FF FF FF FF FF FF FF");
            Serial.println("\nIssues:");
            Serial.println("  [!] All bytes are 0xFF (factory default)");
            Serial.println("  [!] Likely an emulated/spoofed tag");
            Serial.println("\nMitigation:");
            Serial.println("  - Reject tag immediately");
            Serial.println("  - Use cryptographic challenge-response");
            break;
    }

    Serial.println("\n--- SECURITY RECOMMENDATIONS ---");
    Serial.println("1. Never trust NFC tags blindly");
    Serial.println("2. Validate URLs before opening");
    Serial.println("3. Use cryptographic authentication");
    Serial.println("4. Monitor for counter anomalies");

    blinkLED(LED_SECURITY, 5, 100);
    setLEDs(false, true, false);
    securityAlerts++;

    Serial.println("\n========================================");
    Serial.print("Security alerts this session: ");
    Serial.println(securityAlerts);
    Serial.println("========================================\n");
}

// ========== MAIN SETUP AND LOOP ==========

void setup() {
    Serial.begin(115200);
    delay(1000);

    Serial.println("\n");
    Serial.println("========================================");
    Serial.println("     NFC COMMUNICATION LAB");
    Serial.println("     ESP32 Simulation");
    Serial.println("========================================");
    Serial.println("\nThis lab demonstrates NFC concepts:");
    Serial.println("  - Tag types and memory structure");
    Serial.println("  - NDEF message format parsing");
    Serial.println("  - Security validation techniques");
    Serial.println("\n----------------------------------------");
    Serial.println("CONTROLS:");
    Serial.println("  Button 1 (GPIO 12): Read NTAG213");
    Serial.println("  Button 2 (GPIO 13): Read NTAG216 (Smart Poster)");
    Serial.println("  Button 3 (GPIO 14): Read DESFire (secure)");
    Serial.println("  Button 4 (GPIO 15): Malicious tag demo");
    Serial.println("----------------------------------------\n");

    pinMode(BTN_NTAG213, INPUT_PULLUP);
    pinMode(BTN_NTAG216, INPUT_PULLUP);
    pinMode(BTN_DESFIRE, INPUT_PULLUP);
    pinMode(BTN_MALICIOUS, INPUT_PULLUP);

    pinMode(LED_SUCCESS, OUTPUT);
    pinMode(LED_SECURITY, OUTPUT);
    pinMode(LED_PROCESSING, OUTPUT);

    // Startup sequence
    for (int i = 0; i < 3; i++) {
        setLEDs(true, false, false);
        delay(150);
        setLEDs(false, true, false);
        delay(150);
        setLEDs(false, false, true);
        delay(150);
    }
    setLEDs(false, false, false);

    initNTAG213();
    initNTAG216();
    initDESFire();

    Serial.println("\nReady! Press a button to simulate NFC tag reading.\n");
}

void loop() {
    unsigned long currentTime = millis();

    if (currentTime - lastButtonPress < DEBOUNCE_DELAY) {
        return;
    }

    if (digitalRead(BTN_NTAG213) == LOW) {
        lastButtonPress = currentTime;
        simulateReadNTAG213();
    }
    else if (digitalRead(BTN_NTAG216) == LOW) {
        lastButtonPress = currentTime;
        simulateReadNTAG216();
    }
    else if (digitalRead(BTN_DESFIRE) == LOW) {
        lastButtonPress = currentTime;
        simulateReadDESFire();
    }
    else if (digitalRead(BTN_MALICIOUS) == LOW) {
        lastButtonPress = currentTime;
        simulateMaliciousTag();
    }

    // Idle LED pulse
    static unsigned long lastPulse = 0;
    static bool pulseState = false;
    if (currentTime - lastPulse > 2000) {
        pulseState = !pulseState;
        digitalWrite(LED_PROCESSING, pulseState ? HIGH : LOW);
        lastPulse = currentTime;
    }
}

881.3.4 Challenge Exercises

After completing the basic lab, try these advanced challenges:

TipChallenge 1: Add Wi-Fi Handover Record

Modify the NTAG216 initialization to include a Wi-Fi configuration record. The NDEF message should contain: - Network SSID: “IoT-Lab-Network” - Security type: WPA2 - Password: (simulated, not stored in plain text)

Hint: Use the application/vnd.wfa.wsc MIME type for Wi-Fi configuration records.

TipChallenge 2: Implement Counter Verification

Add a function that tracks expected counter values for known tags. When a tag is read: 1. Look up the tag’s UID in a stored database 2. Check if the counter value is higher than the last recorded value 3. If the counter is lower or equal, flag as a potential clone 4. Update the stored counter value after successful validation

Real-world application: This technique is used by NTAG424 DNA for product authentication.

TipChallenge 3: Create a Custom Application

Design and implement a simulated NFC application for one of these scenarios: - Museum Guide: Store exhibit information with multiple language support - Business Card: Create a vCard record with contact details - IoT Device Pairing: Implement Bluetooth handover record

Expected output: Parse and display all record fields in a user-friendly format.

TipChallenge 4: Security Hardening

Enhance the malicious tag detection to check for: 1. URL shorteners: Detect and expand shortened URLs 2. Domain reputation: Check against a whitelist of trusted domains 3. NDEF format validation: Ensure all records follow proper structure 4. Payload size validation: Detect buffer overflow attempts

Security principle: Never trust user-provided data without validation.

881.3.5 Expected Outcomes

After completing this lab, you should be able to:

Skill Demonstration
Tag Type Recognition Identify NTAG213, NTAG216, DESFire by their characteristics
NDEF Parsing Decode URI, Text, and Smart Poster records
Security Analysis Recognize cloned tags, suspicious URLs, and CMAC failures
Memory Calculation Determine if NDEF payload fits in tag memory
Protocol Understanding Explain P2P handshake and handover concepts
NoteTransitioning to Real Hardware

The concepts demonstrated in this simulation directly apply to real NFC development:

Simulation Component Real Hardware Equivalent
Button triggers PN532/RC522 tag detection interrupt
Simulated tag structs Actual tag memory read via SPI/I2C
NDEF parsing functions Libraries like NDEF_MFRC522 or ndef-js
Security validation Server-side CMAC verification
Serial output Mobile app or web dashboard

Recommended hardware for next steps: - PN532 module: Full NFC support (all modes, all tag types) - RC522 module: MIFARE tags only, lower cost - ESP32 + PN532: Complete IoT NFC solution

881.3.6 Knowledge Check

Question 1: In the simulation, NTAG213 has 144 bytes of user memory. If you need to store a Smart Poster with a 50-byte URL, 30-byte title, and icon thumbnail, which tag should you use instead?

Explanation: Smart Posters with multiple nested records (URI + Title + Icon) require more memory than basic NTAG213 provides. NTAG216 with 888 bytes is the appropriate choice for larger payloads. DESFire does not have “unlimited” memory, and Smart Posters do not compress automatically.

Question 2: The DESFire simulation shows CMAC verification. Why is CMAC important for NFC security?

Explanation: CMAC (Cipher-based Message Authentication Code) is a cryptographic mechanism that ensures message integrity and authenticity. It prevents tampering and verifies that the data comes from a legitimate source with the correct cryptographic keys.

Question 3: The malicious tag simulation detected a “counter anomaly” when the counter value was lower than expected. What attack does this indicate?

Explanation: NFC counters increment with each read and cannot be reset. If a tag shows a counter value lower than previously recorded, it indicates the tag was cloned at an earlier point (before the counter reached its current value on the legitimate tag). This is why counters are valuable for anti-cloning protection.

881.4 Summary

This hands-on lab covered:

  • Tag Simulation: NTAG213, NTAG216, and DESFire tag characteristics
  • NDEF Parsing: Decoding URI, Text, and Smart Poster records
  • Security Validation: UID verification, counter checks, CMAC authentication
  • Attack Detection: Cloning, phishing URLs, spoofed UIDs
  • Real Hardware Transition: Mapping simulation concepts to PN532/RC522 development

881.5 What’s Next

Return to the NFC Fundamentals index for links to related NFC chapters, or explore: