HiddenRamblings / TagMo

GNU General Public License v3.0
3k stars 385 forks source link

[Issue]: Power Tag serial number is always 04:07:08:09:10:12:13 #680

Open TheForeignHunter opened 1 year ago

TheForeignHunter commented 1 year ago

Issue Description

I'm getting the "once per day" message in Tears of the Kingdom. I have power tag support turned on and tried editing the properties and randomizing the serial number but that didn't seem to help. NFC Tools is reading the same serial number, 04:07:08:09:10:12:13 which I think is the one used to reset the powertag.

Logcat Output

TagMo 3.7.9 (GitHub Release) #8ff77e4
Google TIRAMISU (13) - 7.00 GB RAM

Usage Requirements

ErdbeerbaerLP commented 1 year ago

In my case this happens in BOTW and TOTK. Tried using two different powertags but that does not change anything. Had to resort back to my NTAG215 stickers. These work without problems, just they are not rewritable obv. Also, using PowerTag A also blocks PowerTag B in my case, so they both have the same serial, also confirmed this with NFC Tools. Also same serial 04:07:08:09:10:12:13

AbandonedCart commented 1 year ago

The code to make the Power Tag compatible with TagMo was long before my time. It was always my understanding, though, that what made it possible was writing the "blank" that acted as a wrapper. A serial number is part of this blank. This would make the serial number seen by games effectively locked.

VocalFan commented 4 months ago

This issue is REALLY annoying as in SSBU, the amiibo is 'cached' in Arenas. Meaning that if I write a new amiibo to the Powertag, SSBU will keep using the old amiibo when I scan unless I exit Online mode and go back in.

VocalFan commented 4 months ago

Might've fixed this, will test first.

VocalFan commented 4 months ago

Okay so ahem... app/src/main/java/com/hiddenramblings/tagmo/nfctech/NfcByte.kt

package com.hiddenramblings.tagmo.nfctech

@Suppress("unused")
object NfcByte {
    const val KEY_FILE_SIZE = 80 // Each key read separately
    const val KEY_RETAIL_SZ = 160 // Both keys read together
    const val TAG_DATA_SIZE = 532 // 540, 572 with signature
    const val TAG_FULL_SIZE = 572 // 540 + 32 byte signature
    const val C1K_DATA_SIZE = 1024
    const val SIGNATURE = 0x21C // 540
    const val PAGE_SIZE = 4
    const val CMD_GET_VERSION = 0x60
    const val CMD_READ = 0x30
    const val CMD_FAST_READ = 0x3A
    const val CMD_WRITE = 0xA2
    const val CMD_COMP_WRITE = 0xA0
    const val CMD_READ_CNT = 0x39
    const val CMD_PWD_AUTH = 0x1B
    const val CMD_READ_SIG = 0x3C

    // N2 Elite - https://wiki.yobi.be/index.php/N2_Elite
    const val N2_GET_VERSION = 0x55
    const val N2_ACTIVATE_BANK = 0xA7
    const val N2_FAST_READ = 0x3B
    const val N2_FAST_WRITE = 0xAE
    const val N2_BANK_COUNT = 0x55
    const val N2_LOCK = 0x46
    const val N2_READ_SIG = 0x43
    const val N2_SET_BANKCOUNT = 0xA9
    const val N2_UNLOCK_1 = 0x44
    const val N2_UNLOCK_2 = 0x45
    const val N2_WRITE = 0xA5
    const val SECTOR_SELECT = 0xC2
    @JvmField
    val POWERTAG_SIGNATURE = TagArray.hexToByteArray(
        "213C65444901602985E9F6B50CACB9C8CA3C4BCD13142711FF571CF01E66BD6F"
    )
    val POWERTAG_IDPAGES: ByteArray
        // Random ID as Powertags can have different IDs!
        get() = generatePowerTagId()
    const val POWERTAG_KEY = "FFFFFFFFFFFFFFFF0000000000000000"
    @JvmField
    val POWERTAG_WRITE = TagArray.hexToByteArray("a000")
    @JvmField
    val POWERTAG_SIG = TagArray.hexToByteArray("3c00")

    private fun generateIDHex(): ByteArray {
        val bytes = ByteArray(8)
        java.util.Random().nextBytes(bytes)
        return bytes
    }

    private fun generatePowerTagId(): ByteArray {
        // Manufacturer ID
        val prefix = "04"
        // Ending padding
        val suffix = "00000000000000"
        // Random ID, my beloved
        val randomHex = generateIDHex().joinToString("") { String.format("%02X", it) }
        // Crunch it together and
        val hexString = prefix + randomHex + suffix
        // Send it off
        return TagArray.hexToByteArray(hexString)
    }
}

This does work in replacing the UID with a unique one, however... We should most likely use the bin file's UID when writing (as I'm pretty sure random generation isn't correct) and on an unused byte maybe the zeroes at the end? add a watermark that way ValidateNtag can read the watermarked bytes and skip the

            if (!compareRange(pages, tagData, 9))
                throw Exception(getString(R.string.fail_mismatch_uid))

Check when using a TagMo-based PowerTag... As well as some cache or something to make getPowerTagKey not fail with uid_key_missing

That should be all that's needed really.

@AbandonedCart

VocalFan commented 4 months ago

A little more detail about the watermark idea:

My current thing is this

prefix + 8 random bytes + suffix of zeroes

04 + 8 random bytes + 00000000000000

But I wonder if we can instead do:

UID of bin file + 00000000004D6F

Which 4D 6F is just "Mo" like from "TagMo" as a watermark.

Which ValidateNtag can check at the location of those two bytes to see if they're 4D 6F which if so, skip the UID mismatch check.

AbandonedCart commented 4 months ago

There should be no reason to skip validation. Instead, there should be a way to validate the data. After all, the entire purpose of that method is to ensure it will work. If 2 bytes can bypass the entire process, there would be no point having it.

Getting the Power Tag key is not something TagMo does for its own purposes, though. The purpose of that is so it remains compatible with not only the Switch, but the Power Tag hardware. I would need to test this against the original hardware.

I will look into this. Thanks.

VocalFan commented 4 months ago

I'll be interested in your progress!