Mobile Dev

Enable NTAG213 Password Protection Android NFC NfcA

Step-by-step guide to set up NTAG213 password protection using Android NFC NfcA. Configure PWD, PACK, AUTH0, ACCESS bytes, perform PWD_AUTH, and test writes with transceive examples and error handling.

1 answer 2 views

How can I correctly enable and test password protection on an NTAG213 tag using Android NFC (NfcA), specifically using PWD_AUTH, PACK and the ACCESS bytes?

Context:

I am developing an Android app that writes data to an NTAG213 via NfcA. Reading and writing work, and I attempted to enable password protection by:

  • Writing a 4-byte password to the PWD page
  • Writing a 2-byte PACK value
  • Modifying the ACCESS byte to protect write operations

After these changes, either the tag still allows writes without authentication, or the tag becomes inaccessible until I remove and re-tap it.

Questions:

  1. Which memory pages on NTAG213 must be written to set up password protection (e.g., PWD, PACK, ACCESS, AUTH0)?
  2. What is the correct bit configuration for the ACCESS byte to require authentication for writes (and optionally reads)? Please include example ACCESS byte values for NTAG213.
  3. Does PWD_AUTH need to be executed before every write operation from Android, or does authentication persist for a session? How does the tag’s session/authentication behavior work in practice?
  4. Are there Android-specific limitations or best practices when performing PWD_AUTH and protected writes with NfcA (timing, reconnecting, transceive payloads, etc.)?
  5. What is a recommended sequence of commands (with example NfcA transceive payloads or steps) to reliably enable and test NTAG213 password protection without locking the tag unintentionally?

Any example sequences, NfcA command frames, and debugging tips to verify that protection is correctly applied would be very helpful.

To enable password protection on an NTAG213 tag via Android NFC with NfcA, write the 4-byte PWD to page 0x2B, 2-byte PACK to 0x2C, set AUTH0 (on page 0x29 byte 3) to your protection start page like 0x09, and configure ACCESS (page 0x2A byte 0) to 0x00 for write-only auth or 0x80 for read+write. Authenticate first with PWD_AUTH (0x1B + password bytes) using nfcA.transceive(), which returns PACK on success—then writes from AUTH0 onward require it, but sessions reset on field loss so re-auth per tap in Android. Test by attempting protected writes pre/post-auth; common fixes include power-cycling the tag and handling TagLostExceptions.


Contents


NTAG213 Memory Layout for Password Protection

NTAG213 tags pack 144 user bytes across 40 pages (E2h total), but password setup lives in the config sector starting around page 0x29. Why these pages? They’re reserved for protection features per the NXP specs—ignore them and your writes slide right through unprotected.

Key pages you must hit:

  • PWD (page 0x2B): 4-byte password. Default all zeros, so pick something strong like {0x12, 0x34, 0x56, 0x78}.
  • PACK (page 0x2C): 2-byte password acknowledgment—tag echoes this back on successful PWD_AUTH. Bytes 2-3 are RFUI (mirror UID) and PROT (unused here).
  • AUTH0 (page 0x29, byte 3): Sets the starting page for protection. Value 0x09 protects from page 9 (NDEF area) onward; max FFh, but don’t exceed user memory or auth disables entirely, as noted in the Eccel guide.
  • ACCESS (page 0x2A, byte 0): Enables protection mode. More on bits below.

Pages 0x2A-0x2C stay writable even post-setup unless you lock CFGLCK (page 0x28 byte 3), which is permanent—don’t touch it lightly. Got a tag in hand? Fast-read pages 0x28-0x2D first with 0x3A 0x28 0x2D to baseline.


ACCESS and AUTH0: Exact Bit Configurations

The ACCESS byte is a single powerhouse on page 0x2A byte 0. Break it down bit-by-bit:

Bit Name Effect
7 PROT 1 = protect reads too (with writes); 0 = writes only
6 RPM_LIM Limits failed auth attempts (AUTHLIM counter)
5-2 - Reserved (0)
1-0 RPM 00=off, 01=reads from AUTH0, 10=writes from AUTH0, 11=both

Examples for NTAG213:

  • Write-only protection: ACCESS = 0x0F (RPM=10 binary for writes from AUTH0, PROT=0). Or simpler, 0x00 if you’re not using RPM_LIM.
  • Read+write protection: 0x8F (PROT=1 + RPM=11). Eccel uses 0x80 for full lock from AUTH0.

AUTH0 byte (0x29[3]): Straight page address. Set to 0x09, write {0xA2, 0x29, 0x00, 0x00, 0x09, 0x00, 0x00} via transceive (WRITE cmd 0xA2). Forget this, and protection skips your data pages—classic gotcha from Stack Overflow threads.

Power cycle (remove/re-tap) after writes to activate. Reads below AUTH0 stay open.


PWD_AUTH Mechanics and Session Behavior

PWD_AUTH unlocks the tag: send 0x1B + your 4-byte PWD via nfcA.transceive(). Success? Tag replies with PACK (your 2 bytes) + RFUI/PROT. Fail? NAK (0x00/01/04/05), often crashing Android with IOException.

Does auth stick around? Session-based, but picky. It persists across commands while the field stays on—no timeout spec, but in practice, 10-30 seconds or until field loss. Android NFC? Every re-tap resets it. You’ll re-auth before each protected write session. Why? Tag loses power on detag, session vanishes.

From NXP community: Auth covers ops from AUTH0 onward until reset. Test: Auth once, spam writes. Detag/retag, try write—fails without re-auth.


Android NfcA Pitfalls and Best Practices

Android’s NfcA shines for low-level NTAG213 control, but it’s finicky. transceive(byte[]) appends CRC auto, so payloads are cmd + data only.

Key gotchas:

  • NAKs/Exceptions: Wrong PWD_AUTH? Tag sends NAK, Android throws TagLostException or IOException. Catch, check response[0]: 0x00=NAK0 (cmd error), 0x04=NAK4 (auth fail). This SO post shows parsing.
  • Reconnects: Post-NAK or long ops, tag “freezes”—nfcA.connect() again. Always wrap in try-catch with reconnect.
  • Timing: No delays needed, but field must be stable. Avoid Fast Read (0x3A) on protected pages pre-auth—IOException city, per AndroidCrypto Medium.
  • Payloads: WRITE: {0xA2, page, b0,b1,b2,b3}. PWD_AUTH: {0x1B, pwd0,pwd1,pwd2,pwd3}.
  • Best practice: Read config first, set AUTH0 to PWD page (0x2B) post-setup for self-protection, reconnect on errors.

Pro tip: Test on emulator? Nope—real hardware only.


Step-by-Step Setup Sequence with Transceive Examples

Here’s your bulletproof sequence. Assume fresh NTAG213, NfcA connected.

  1. Baseline read: transceive(new byte[] {0x30, 0x2A}) for ACCESS page.
  2. Write ACCESS: transceive(new byte[] {0xA2, 0x2A, 0x00, 0x00, 0x00, 0x00}) (write-only).
  3. Write AUTH0: transceive(new byte[] {0xA2, 0x29, 0x00, 0x00, 0x09, 0x00}) (protect from 9).
  4. Write PWD: transceive(new byte[] {0xA2, 0x2B, 0x12, 0x34, 0x56, 0x78}).
  5. Write PACK: transceive(new byte[] {0xA2, 0x2C, 0xAB, 0xCD, 0x00, 0x00}).
  6. Power cycle: Remove/re-tap, reconnect NfcA.
  7. Auth: byte[] authCmd = {0x1B, 0x12, 0x34, 0x56, 0x78}; byte[] pack = nfcA.transceive(authCmd); Expect {0xAB, 0xCD, …}.
  8. Test write: To page 0x0E: transceive(new byte[] {0xA2, 0x0E, 0xDE, 0xAD, 0xBE, 0xEF})—succeeds post-auth.
  9. Test fail: Detag/retag, skip auth, same write—NAK.

Verify: Read page 0x2A post-setup. Matches Eccel steps.


Debugging and Common Mistakes

Tag still writable? AUTH0 unset (defaults 0x00, skips protection). Check this SO fix.

Inaccessible? Bad ACCESS (e.g., PROT=1 too early) or CFGLCK. Recovery: AUTH0 > user pages disables; rewrite unprotected.

Exceptions galore? SO error handling: Parse responses, reconnect:

kotlin
try {
 byte[] resp = nfcA.transceive(cmd);
 if (resp[0] == 0x00 || resp[0] == 0x04) { /* NAK */ }
} catch (Exception e) {
 nfcA.close(); nfcA.connect(); // Reconnect
}

PACK mismatch? Tag echoes your PACK—double-check write.

Checklist: Power cycle always, auth before tests, readback configs.


Complete Kotlin Code Example

kotlin
private fun setupPasswordProtection(nfcA: NfcA, pwd: ByteArray, pack: ByteArray, startPage: Int): Boolean {
 try {
 // 2. ACCESS 0x00 (write-only)
 nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x2A, 0x00, 0x00, 0x00, 0x00))
 // 3. AUTH0
 nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x29, 0x00, 0x00, startPage.toByte(), 0x00))
 // 4. PWD
 nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x2B, pwd[0], pwd[1], pwd[2], pwd[3]))
 // 5. PACK
 nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x2C, pack[0], pack[1], 0, 0))
 return true
 } catch (e: Exception) {
 reconnectNfcA(nfcA)
 return false
 }
}

private fun authenticate(nfcA: NfcA, pwd: ByteArray): ByteArray? {
 return try {
 val cmd = byteArrayOf(0x1B.toByte(), pwd[0], pwd[1], pwd[2], pwd[3])
 nfcA.transceive(cmd)
 } catch (e: IOException) {
 Log.e("NFC", "Auth fail: ${e.message}")
 null
 }
}

private fun reconnectNfcA(nfcA: NfcA) {
 try { nfcA.close() } catch (ignored: Exception) {}
 nfcA.connect()
}

Tweak for your Intent. See GitHub repo for more.



Sources

  1. Eccel: How to set a password for NTAG2xx
  2. Stack Overflow: Enable NTAG213 password with AUTH0
  3. Stack Overflow: Setting NTAG213 password
  4. Stack Overflow: NTAG213 auth exceptions
  5. AndroidCrypto Medium: NfcA guide
  6. NXP Community: PWD_AUTH Android
  7. Android Advanced NFC GitHub

Conclusion

Nail NTAG213 password protection on Android NFC by hitting pages 0x29-0x2C in order—AUTH0 first, then PWD/PACK—power cycle, and auth with PWD_AUTH before writes. ACCESS 0x00 keeps reads open while locking writes from your start page; sessions reset on every tap, so wrap ops in reconnect logic. Test ruthlessly: failed writes post-detach confirm success. Grab a tag, run the code, and lock down those Android NFC apps today—no more unprotected data slips.

Authors
Verified by moderation
Moderation
Enable NTAG213 Password Protection Android NFC NfcA