Skip to content

iOS: How to replicate Android NTAG 424 SDM (decrypt PICC + verify CMAC + get UID) #18

Description

@Ramoliya

I have a Flutter app with NTAG 424 SDM (SUN) support.

On the Flutter side I only call a MethodChannel (readTagUrl) and receive a tagData map back.
All the real SDM / crypto work is done on the native side (Android now, and I want the same on iOS).

Android (already working, native side):

On Android I do all logic in MainActivity (Kotlin):

Read NDEF URL
Extract picc_data and cmac from the URL
Decrypt picc_data with my AES key to get:
PICC UID
Read counter
Verify the CMAC with the same key
Compare decrypted UID with tag.id (raw NFC tag UID)
Send the result back to Flutter as a map

Core Android logic (simplified, key removed):

val tagIdHex1 = bytesToHex(tag.id) // raw NFC tag UID

val piccData = PiccData.decodeFromEncryptedBytes(
Utils.hexStringToByteArray(piccDataHex),
oneAESKey,
false
)

val uidHex = bytesToHex(piccData.uid) // decrypted PICC UID
val counter = piccData.readCounter
val tagIdHex = bytesToHex(tag.id)
val isUidValid = uidHex.equals(tagIdHex, ignoreCase = true)

piccData.setMacFileKey(oneAESKey)
val isValid = piccData.performShortCMAC(null)
.contentEquals(Utils.hexStringToByteArray(cmacHex))

From Android I send back to Flutter:

tag_id (raw tag UID)
picc_encrypted
cmac
uid (decrypted UID)
counter
cmac_valid
full_url

iOS (current native side):

On iOS I have started implementing the native side in AppDelegate.swift:

Using NFCNDEFReaderSession to read the NDEF URI
I parse picc_data, cmac, and full_url from the URL
I send a map back to Flutter via MethodChannel("ntag424/sun") and readTagUrl

Right now, the map I send from iOS looks like:

[
"tag_id": "", // empty for now
"picc_encrypted": piccDataHex,
"cmac": cmacHex,
"uid": "", // not decrypted on iOS yet
"counter": 0,
"cmac_valid": false,
"full_url": url
]

So on iOS I only do URL parsing.
I do not yet:

get a raw tag UID comparable to Android’s tag.id
decrypt picc_encrypted into UID + counter
verify CMAC on-device

My requirements on iOS (to match Android behavior):

On the iOS native side (Swift, with NfcDnaKit imported) I want to:

Get a tag UID that I can compare with the decrypted UID (Android’s tag.id equivalent).
Decrypt picc_data (PICC_ENC) from the URL using my 16‑byte AES key to obtain:
PICC UID
read counter
Compute and verify CMAC against the cmac value from the URL.
Then, just like Android, send this full map back to Flutter via MethodChannel.

Questions (concrete):

With NfcDnaKit, what is the recommended way on iOS to get a NTAG 424 tag UID that I can compare to the decrypted UID?
Should I use NFCTagReaderSession + DnaCommunicator.getChipUid() for this?

Does NfcDnaKit provide any helper or example code to:
decrypt SDM picc_data (PICC_ENC) into UID + counter, and
compute/verify the SDM CMAC, given a known 16‑byte AES key?

If there is no built‑in helper, what is the correct pattern you recommend on iOS:
implement an iOS/Swift equivalent of the Android PiccData.decodeFromEncryptedBytes and performShortCMAC using your AES/CMAC utilities, or
rely on EV2 communication (DnaCommunicator / nxpEncryptedCommand) to read UID + counter directly from the tag and combine this with the NDEF URL data?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions