From a49a12742bbe2c519a1f60e8f0bd147a00b47469 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Thu, 22 Jan 2026 13:19:05 +0100 Subject: [PATCH 1/3] Allow to wipe NFC tags --- .../libpretixnfc/highlevel/PretixMf0aes.kt | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/libpretixnfc/src/main/java/eu/pretix/libpretixnfc/highlevel/PretixMf0aes.kt b/libpretixnfc/src/main/java/eu/pretix/libpretixnfc/highlevel/PretixMf0aes.kt index fdde57e..58a450a 100644 --- a/libpretixnfc/src/main/java/eu/pretix/libpretixnfc/highlevel/PretixMf0aes.kt +++ b/libpretixnfc/src/main/java/eu/pretix/libpretixnfc/highlevel/PretixMf0aes.kt @@ -29,7 +29,7 @@ class PretixMf0aes(val keySets: List, val useRandomIdForNewTags: B Page 0x04: public_id (big endian) */ - fun process(nfca: AbstractNfcA, encodeWith: Mf0aesKeySet? = null): String { + fun process(nfca: AbstractNfcA, encodeWith: Mf0aesKeySet? = null, wipe: Boolean = false): String { nfca.connect() try { val tagType = GetVersion().execute(nfca) as? UltralightAESTagType @@ -39,7 +39,7 @@ class PretixMf0aes(val keySets: List, val useRandomIdForNewTags: B val firstPage = readPages(nfca, 0x04, 1) if (firstPage.contentEquals(byteArrayOf(0, 0, 0, 0))) { - if (encodeWith != null) { + if (encodeWith != null && !wipe) { return encode(nfca, encodeWith) } else { throw NfcChipReadError(ChipReadError.EMPTY_CHIP) @@ -48,7 +48,12 @@ class PretixMf0aes(val keySets: List, val useRandomIdForNewTags: B for (keySet in keySets) { val expectedPage = idToBytes(keySet.publicId) if (firstPage.contentEquals(expectedPage)) { - return decode(nfca, keySet) + return if (wipe) { + wipe(nfca, keySet) + "OK" + } else { + decode(nfca, keySet) + } } } throw NfcChipReadError(ChipReadError.FOREIGN_CHIP) @@ -94,6 +99,51 @@ class PretixMf0aes(val keySets: List, val useRandomIdForNewTags: B return uid.toHexString(false) } + + private fun wipe(nfca: AbstractNfcA, keySet: Mf0aesKeySet) { + var authenticatedNfcA = AuthenticationHelper( + AuthenticationHelper.KeyId.UID_RETR_KEY, + nfca, + keySet.uidKey + ).authenticate() + val uid = UidHelper(authenticatedNfcA).readUid() + + val diversifiedKey = An10922KeyDiversification().generateDiversifiedKeyAES128( + keySet.diversificationKey, + uid, // 4 bytes + "eu.pretix".toByteArray(Charset.defaultCharset()), // 9 bytes + idToBytes(keySet.publicId) // 4 bytes + // total diversification input: 17 bytes (must be 16..32) + ) + + authenticatedNfcA = AuthenticationHelper( + AuthenticationHelper.KeyId.DATA_PROT_KEY, + authenticatedNfcA, + diversifiedKey + ).authenticate() + + MifareUltralightAesConfigChange.Builder() + .setDataProtKey(ByteArray(16) {0}) + .setUidRetrKey(ByteArray(16) {0}) + .setSecurityOptions( + randomIdActive = false, + secureMessagingActive = false, + auth0 = MifareUltralightAesConfigChange.AUTH0_DEFAULT + ) + .setProtectionOptions( + protectReads = false, + lockUserConfig = false, + counter2IncrementWithoutAuthEnabled = false, + counter2ReadWithoutAuthEnabled = false, + vctId = 0x05, + authLim = 0 + ) + .build() + .write(authenticatedNfcA) + + writePages(authenticatedNfcA, 0x04, ByteArray(35 * 4) { 0 }) + } + private fun encode(nfca: AbstractNfcA, keySet: Mf0aesKeySet): String { var uid = UidHelper(nfca).readUid() if (uid[0] == 0x08.toByte()) { From 747f5f38a3041ed8407f004635169d59f658f731 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Thu, 22 Jan 2026 13:38:27 +0100 Subject: [PATCH 2/3] Add parameter names also to encoding for better readability --- .../libpretixnfc/highlevel/PretixMf0aes.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libpretixnfc/src/main/java/eu/pretix/libpretixnfc/highlevel/PretixMf0aes.kt b/libpretixnfc/src/main/java/eu/pretix/libpretixnfc/highlevel/PretixMf0aes.kt index 58a450a..f4319dd 100644 --- a/libpretixnfc/src/main/java/eu/pretix/libpretixnfc/highlevel/PretixMf0aes.kt +++ b/libpretixnfc/src/main/java/eu/pretix/libpretixnfc/highlevel/PretixMf0aes.kt @@ -174,18 +174,18 @@ class PretixMf0aes(val keySets: List, val useRandomIdForNewTags: B .setDataProtKey(diversifiedKey) .setUidRetrKey(keySet.uidKey) .setSecurityOptions( - useRandomIdForNewTags, - true, + randomIdActive = useRandomIdForNewTags, + secureMessagingActive = true, // Don't write-protect chip in debug mode so it can be easily recovered and reused - if (debug) MifareUltralightAesConfigChange.AUTH0_DEFAULT else 0x04 + auth0 = if (debug) MifareUltralightAesConfigChange.AUTH0_DEFAULT else 0x04 ) .setProtectionOptions( - false, - false, - false, - false, - 0x05, - 256 + protectReads = false, + lockUserConfig = false, + counter2IncrementWithoutAuthEnabled = false, + counter2ReadWithoutAuthEnabled = false, + vctId = 0x05, + authLim = 256 ) .build() .write(nfca) From 6473ad5b78b09743152478435f39c9efa6191fc3 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Tue, 5 May 2026 10:18:38 +0200 Subject: [PATCH 3/3] Add missing code to actually enable wiping... --- .../android/hardware/AndroidNativeNfcHandler.kt | 3 ++- .../libpretixnfc/android/hardware/NfcHandler.kt | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libpretixnfc-android/src/main/java/eu/pretix/libpretixnfc/android/hardware/AndroidNativeNfcHandler.kt b/libpretixnfc-android/src/main/java/eu/pretix/libpretixnfc/android/hardware/AndroidNativeNfcHandler.kt index ca8269f..230357a 100644 --- a/libpretixnfc-android/src/main/java/eu/pretix/libpretixnfc/android/hardware/AndroidNativeNfcHandler.kt +++ b/libpretixnfc-android/src/main/java/eu/pretix/libpretixnfc/android/hardware/AndroidNativeNfcHandler.kt @@ -34,7 +34,8 @@ private var lastTagTime: Long = 0 enum class NfcHandlerMode { DEFAULT, - ENCODE + ENCODE, + WIPE } diff --git a/libpretixnfc-android/src/main/java/eu/pretix/libpretixnfc/android/hardware/NfcHandler.kt b/libpretixnfc-android/src/main/java/eu/pretix/libpretixnfc/android/hardware/NfcHandler.kt index a7b1e7c..bca7416 100644 --- a/libpretixnfc-android/src/main/java/eu/pretix/libpretixnfc/android/hardware/NfcHandler.kt +++ b/libpretixnfc-android/src/main/java/eu/pretix/libpretixnfc/android/hardware/NfcHandler.kt @@ -50,11 +50,14 @@ fun getNfcHandler(activity: Activity, keySets: List, useRandomIdFo } fun processMf0aes(keySets: List, mode: NfcHandlerMode, useRandomIdForNewTags: Boolean = false, nfca: AbstractNfcA): String { - return PretixMf0aes( + val mf0aes = PretixMf0aes( keySets, useRandomIdForNewTags, BuildConfig.DEBUG, - ).process( - nfca, - encodeWith = if (mode == NfcHandlerMode.ENCODE) keySets.firstOrNull { it.canEncode } else null) + ) + return when (mode) { + NfcHandlerMode.DEFAULT -> mf0aes.process(nfca) + NfcHandlerMode.ENCODE -> mf0aes.process(nfca, keySets.firstOrNull { it.canEncode }) + NfcHandlerMode.WIPE -> mf0aes.process(nfca, wipe=true) + } }