(Disclaimer: I new in NFC and I'm trying to create test apps to learn)
As far as I know, Ndef can only be protected from being written. I investigated it and, apparently, this is only posible with quite hard low-level commands.
However, my code uses Ndef, NdefMessage and NdefFormatable classes. I'm not using anything low-level. Is this possible without low-level commands?
override fun onResume() {
super.onResume()
if (nfcAdapter != null) {
val options = Bundle()
// Work around for some broken Nfc firmware implementations that poll the card too fast
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250)
// Enable ReaderMode for all types of card and disable platform sounds
nfcAdapter!!.enableReaderMode(
this,
this::onTagDiscovered,
NfcAdapter.FLAG_READER_NFC_A or
NfcAdapter.FLAG_READER_NFC_B or
NfcAdapter.FLAG_READER_NFC_F or
NfcAdapter.FLAG_READER_NFC_V or
NfcAdapter.FLAG_READER_NFC_BARCODE or
NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
options
)
}
}
override fun onPause() {
super.onPause()
}
fun NFC_siempre_puesto(tag: Tag?){
}
fun onTagDiscovered(tag: Tag?) {
// Read and or write to Tag here to the appropriate Tag Technology type class
// in this example the card should be an Ndef Technology Type
try {
val mNdef: Ndef = Ndef.get(tag)
val mNdefMessage: NdefMessage = mNdef.getCachedNdefMessage()
Log.d("TestNFC",String(mNdefMessage.records[0].payload, Charsets.UTF_8).split(";")[0])
Log.d("TestNFC",("ensecurityCode:"+ resources.getString(R.string.security_code_cards)))
if(String(mNdefMessage.records[0].payload, Charsets.UTF_8).split(";")[0].contains("securityCode:"+ resources.getString(R.string.security_code_cards))){
text_view_nfc!!.text = String(mNdefMessage.records[0].payload, Charsets.UTF_8)
}else{
text_view_nfc!!.text = "[Esta carta no pertenece a esta App]"
}
}catch (e: FormatException) {
// if the NDEF Message to write is malformed
} catch (e: TagLostException) {
// Tag went out of range before operations were complete
} catch (e: NullPointerException) {
text_view_nfc!!.text = "[Error de lectura. Intentando reiniciar etiqueta...]"
val mRecord: NdefRecord = NdefRecord.createTextRecord("en", "securityCode:"+ resources.getString(R.string.security_code_cards)+";Version:1")
val mMsg = NdefMessage(mRecord)
val mNdef: NdefFormatable = NdefFormatable.get(tag)
mNdef.connect()
mNdef.format(mMsg)
// Success if got to here
runOnUiThread {
Toast.makeText(
getApplicationContext(),
"Etiqueta reiniciada con éxito. Reeleyendo...",
Toast.LENGTH_SHORT
).show()
}
mNdef.close()
nfcAdapter!!.disableReaderMode(this)
val options = Bundle()
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250)
nfcAdapter!!.enableReaderMode(
this,
this::onTagDiscovered,
NfcAdapter.FLAG_READER_NFC_A or
NfcAdapter.FLAG_READER_NFC_B or
NfcAdapter.FLAG_READER_NFC_F or
NfcAdapter.FLAG_READER_NFC_V or
NfcAdapter.FLAG_READER_NFC_BARCODE or
NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
options
)
} catch (e: IOException) {
// if there is an I/O failure, or the operation is cancelled
} finally {
// Be nice and try and close the tag to
/* Disable I/O operations to the tag from this TagTechnology object, and release resources.
try {
mNdef.close()
} catch (e: IOException) {
// if there is an I/O failure, or the operation is cancelled
}*/
}
}
}
This is the code and the functions I'm using. (This is a test APP to learn about NFC) It looks for a NFC tag and checks if it has a code inside. If the program cannot understand the card (because is empty or invalid), tries to reset it by writting the needed code:.
val mRecord: NdefRecord = NdefRecord.createTextRecord("en", "securityCode:"+ resources.getString(R.string.security_code_cards)+";Version:1")
val mMsg = NdefMessage(mRecord)
val mNdef: NdefFormatable = NdefFormatable.get(tag)
mNdef.connect()
...
However, I cannot allow other apps to modify this. I need to protect this to make my app the only able to modify the card. (Since the cards are designed to only work with this app). The cards have this technology: Image of my card's info
(Note: Ndef technology turns into NdefFormatable when the cards are empty, that's why I catch the NullException when calling Ndef and write it with NdefFormatable)
I have found this post that explains the low-level commands, but, to be honest, I don't even know how to implement that in my code.
You can only make Ndef readonly and this should be a irreversible process, though as you are using Mifare Classic Tag support of this Tag is optional (therefore it might not be read by all phones) and it is optional because it uses a proprietary implementation, so I don't know how well it support the standard for making it readonly.
So you will have to use the low-level capabilities of the Tag to implement password protection because how password protect is done it different for all the Tag types available.
As pointer in the direction of how to send low level commands (because I avoid Mifare Classic cards as the are non standard and not 100% supported)
Where you do val mNdef: Ndef = Ndef.get(tag)
you should do something like:-
val mMifare: MifareClassic = MifareClassic.get(tag)
mMifare.writeBlock(someInt, someByteArray)
or possibly:-
val mResultByteArray = mMifare.transceive(someByteArray)
Details of which blocks to write to / commands you need to send to configure and use a password is in the other answer you found.
(I'm assuming that the particular phone you are using supports MifareClassic
as you are using Ndef
on it, for phones that don't support MifareClassic
you will need to use the NfcA
class a lot)