New interoperability for Activision Skylanders NFC toys

Demonstration: writing on a Mac, reading on a Raspberry Pi

This demo shows new interoperability of Activision Skylanders NFC toys. Previously, only Activision hardware and games had the ability to write data to Skylanders NFC toys.

Any NFC reader can read the ATQA, SAK and UID of an NFC toy. These let you tell it's a Skylander, and its unique ID.

By knowing the algorithm used to set the read/write passwords (keys A), we can interoperably read/write our own data to a Skylanders NFC toy.

This video first shows a program which computes the sixteen (16) keys A and uses them to write new, custom text to a Skylanders NFC toy. The program is running on a standard Apple laptop, using a commercial, off-the-shelf, USB NFC reader (the Identiv SCL3711).

Then, a standard Raspberry Pi computer is shown, using a commercial, off-the-shelf, NFC add-on (the ITEAD PN532 NFC module). A program running on the Raspberry Pi computes the same sixteen (16) keys A, and uses them to read the custom text from the toy.

The video has artificial delays inserted so the on-screen explanations can be read; computing the keys A and reading and writing to the NFC toy are nearly instantaneous otherwise.

Read Writing your own data to an Activision Skylanders NFC toy to see a step-by-step workflow similar to the demo video, using standard software available on any Mac or Linux computer.

Computing the keys A

Since 2014, researchers have developed at least a couple of ways to generate the keys A for Activision Skylanders toys. The simplest version is a standard 64-bit CRC, reduced to operate on only 48 bits.

A clean room description of the algorithm is as follows:

The key A for sector 0 is always the 6-byte (12-character) hexadecimal representation of the integer computed by the multiplication of the three prime numbers 73 and 2017 and 560,381,651

For all other sectors, let a big-endian, most-significant-bit first, 48-bit CRC computation use the ECMA-182 polynomial of 0x42f0e1eba9ea3693, and not be reflected or reversed or have a final register XOR value; this is equivalent to a CRC64-ECMA-182 with left shift, MSB check and remainder trim reduced from 64 to 48 bits

Let the initial value of the CRC48 register be the value of the integer computed by the multiplication of the five prime numbers 2 and 2 and 3 and 1103 and 12,868,356,821

Compute the CRC48 of the 5 bytes encoded by the 10-character hexadecimal concatenation of the UID and the sector number in hexadecimal

The key A for that sector is 6 bytes, represented in hexadecimal as 12 characters: the result of the CRC48 with the hexadecimal bytes' order reversed

Sample inputs/outputs
UID Sector Key A
ff81bab9 1 83018b0e7d13
c47260f8 3 ab04476c4c95
96506199 15 f8905a892508
BF2EE007 6 8d30722433ca

An example of this implemented in Python 2 is as follows:


## - Compute a key A
## Written in 2016 and 2017 and 2018 by Vitorio Miliano
## To the extent possible under law, the author has dedicated all
## copyright and related and neighboring rights to this software to
## the public domain worldwide.  This software is distributed without
## any warranty.
## You should have received a copy of the CC0 Public Domain
## Dedication along with this software.  If not, see
## <>.

import binascii, re, struct, sys

uidre = re.compile('^[0-9a-f]{8}$', re.IGNORECASE)
magic_nums = [2, 3, 73, 1103, 2017, 560381651, 12868356821]

# Standard MSB CRC pseudocode e.g.
# CRC64 ECMA-182 e.g.
def pseudo_crc48(crc, data):
    POLY = 0x42f0e1eba9ea3693
    MSB = 0x800000000000
    TRIM = 0xffffffffffff
    for x in data:
        crc = crc ^ (x << 40)
        for k in range(0, 8):
            if crc & MSB:
                crc = (crc << 1) ^ POLY
                crc = crc << 1
            crc = crc & TRIM
    return crc

def calc_keya(uid, sector):
    if sector == 0:
        return format(magic_nums[2] * magic_nums[4] * magic_nums[5], '012x')

    if uidre.match(uid) is None:
        raise ValueError('invalid UID (four hex bytes)')

    if sector < 0 or sector > 15:
        raise ValueError('invalid sector (0-15)')

    PRE = magic_nums[0] * magic_nums[0] * magic_nums[1] * magic_nums[3] * magic_nums[6]
    ints = [ord(a) for a in uid.decode('hex')] + [sector]

    key = pseudo_crc48(PRE, ints)

    return binascii.hexlify(struct.pack('<Q', key))[0:12]

if __name__ == '__main__':
    if len(sys.argv) > 1:
        keysa = []
        for sector in range(0, 16):
            keysa.append(calc_keya(sys.argv[1], sector))
        if len(sys.argv) > 2 and sys.argv[2] == '-eml':
            print ('0'*20+'\n'+('0'*32+'\n')*3).join(keysa).join([(sys.argv[1]+'0'*24+'\n')+(('0'*32+'\n')*2), '0'*20])
            print '\n'.join(keysa)

Use at your own risk

This has only been tested with figures from previous Skylanders games: Spyro's Adventure, Giants, Swap Force, Trap Team, and SuperChargers.

Writing custom data to an Activision Skylanders NFC toy will result in the game (Spyro's Adventure, Giants, Swap Force, Trap Team, or SuperChargers) not recognizing the figure. Completely erasing the NFC toy will allow the game to recognize it again (as a new toy).

This has not been tested with Imaginators figures or the Imaginators game. Other researchers have found additional security data stored on Imaginators figures, matching the recommendations in Kevin Valk's masters thesis, Comprehensive security analyses of a toys-to-life game and possible countermeasures. Erasing or overwriting this data will render an Imaginators toy unable to be used with the Imaginators game in the future.