Difference between revisions of "NFC adapter"

From 3dbrew
Jump to navigation Jump to search
(Did a lot of research on layer 3 packets by sniffing the connection between the NFC adapter and the 3DS using an IRda usb adapter. Been able to spoof the nfc adapter using the same adapter (see github link at bottom of page))
 
(11 intermediate revisions by 2 users not shown)
Line 1: Line 1:
The Nintendo NFC adapter is an upcoming external device which adds NFC capabilities for [[amiibo]]s to old Nintendo 3DS and Nintendo 2DS consoles, using the infrared port on the back of the console.
+
The Nintendo NFC adapter, formally '''Nintendo NFC Reader/Writer'''  and codenamed '''Fangate''', is an external device which adds NFC capabilities for [[amiibo]]s to old Nintendo 3DS and Nintendo 2DS consoles, using the infrared port on the back of the console.
 +
 
 +
It launched simultaneously with Animal Crossing Happy Home Designer, with which it's optionally bundled; it can also be bought standalone at a nominal price of 21 €.
  
 
[[File:150217nfcreader-640x360.jpg|thumb|right|250px|Media preview, as released by Nintendo]]
 
[[File:150217nfcreader-640x360.jpg|thumb|right|250px|Media preview, as released by Nintendo]]
Line 7: Line 9:
 
Based on analysis of the [[fangate_updater.bin]] file, which is part of the old Nintendo 3DS operating system since [[9.3.0-21]] and contains the firmware running on the external adapter; and analysis of the [[NFC Services]] running on old 3DS.
 
Based on analysis of the [[fangate_updater.bin]] file, which is part of the old Nintendo 3DS operating system since [[9.3.0-21]] and contains the firmware running on the external adapter; and analysis of the [[NFC Services]] running on old 3DS.
  
* SOC inside the adapter: Broadcom BCM20791B1
+
* SOC inside the adapter: Broadcom BCM20791B1 or ST proprietary "MCU-FGT/rev.A/GH24S VQ"
 
* CPU: ARM Cortex M0
 
* CPU: ARM Cortex M0
 
* Communications: infrared, with [[ir:USER]] running on the console. Uses obfuscated payloads. Baud rate is 115200 bps.
 
* Communications: infrared, with [[ir:USER]] running on the console. Uses obfuscated payloads. Baud rate is 115200 bps.
Line 38: Line 40:
 
Packets are sent using IrDA-SIR (using [[ir:USER]]), with a 8N1 encoding (eight data bits, one stop bit, without parity). Each one is formed by a 2-byte header, a varint with the payload size, an obfuscated payload, and trailing error detection byte.
 
Packets are sent using IrDA-SIR (using [[ir:USER]]), with a 8N1 encoding (eight data bits, one stop bit, without parity). Each one is formed by a 2-byte header, a varint with the payload size, an obfuscated payload, and trailing error detection byte.
  
== Packet header ==
+
== Layer 1 - framing format ==
The packet header is fixed and consists in a synchronization byte (0xA5), followed by a unused (possibly RFU) zero byte. After these two hardcoded bytes, there's a varint representing the payload size, which may use one byte or two, depending on the how big the payload is.
+
Frames are encoded using two different yet very simmilar formats, depending on how large the payload to be transmitted is:
 
 
 
* For payloads with less than 64 bytes, the third byte represents the payload size.
 
* For payloads with less than 64 bytes, the third byte represents the payload size.
 
* For packets with up to 16383 bytes, the size is split in two bytes, with the third byte being the upper 6 bits of the payload size, OR'd with 0x40, and the fourth being the lower eight bits of the payload size
 
* For packets with up to 16383 bytes, the size is split in two bytes, with the third byte being the upper 6 bits of the payload size, OR'd with 0x40, and the fourth being the lower eight bits of the payload size
  
For packets with less than 64 bytes:
+
{| class="wikitable" style="margin: 0 auto; text-align: center;"
{| class="wikitable" border="1"
+
|+IR framing format - short frame
! Sync
+
|-
! RFU
+
! style="border-top: none" | Byte
! Size
+
! style="width:10%;"| 7
 +
! style="width:10%;"| 6
 +
! style="width:10%;"| 5
 +
! style="width:10%;"| 4
 +
! style="width:10%;"| 3
 +
! style="width:10%;"| 2
 +
! style="width:10%;"| 1
 +
! style="width:10%;"| 0
 +
|-
 +
! 0x00
 +
| colspan="8"|Synchronization byte (<code>0xA5</code>)
 +
|-
 +
! 0x01
 +
| colspan="8"|Reserved for future use (<code>0x00</code>)
 +
|-
 +
! 0x02
 +
| RFU (<code>0</code>)
 +
| Short frame (<code>0</code>)
 +
| colspan="6"|Payload size
 +
|-
 +
! 0x03
 +
| colspan="8"|Payload byte 0
 +
|-
 +
| colspan="9"|...
 +
|-
 +
! 0x03+n-1
 +
| colspan="8"|Payload byte n-1
 
|-
 
|-
| 0xA5
+
! 0x03+n
| 0x00
+
| colspan="8"|[[CRC-8-CCITT]] computer over whole packet
| size
 
 
|}
 
|}
  
For packets with up to 16383 bytes:
+
 
{| class="wikitable" border="1"
+
{| class="wikitable" style="margin: 0 auto; text-align: center;"
! Sync
+
|+IR framing format - long frame
! RFU
+
|-
! Size (1)
+
! style="border-top: none" | Byte
! Size (2)
+
! style="width:10%;"| 7
 +
! style="width:10%;"| 6
 +
! style="width:10%;"| 5
 +
! style="width:10%;"| 4
 +
! style="width:10%;"| 3
 +
! style="width:10%;"| 2
 +
! style="width:10%;"| 1
 +
! style="width:10%;"| 0
 +
|-
 +
! 0x00
 +
| colspan="8"|Synchronization byte: <code>0xA5</code>
 +
|-
 +
! 0x01
 +
| colspan="8"|Reserved for future use
 +
|-
 +
! 0x02
 +
| RFU (<code>0</code>)
 +
| Long frame (<code>1</code>)
 +
| colspan="6"|Payload size (upper 6 bits)
 +
|-
 +
! 0x03
 +
| colspan="8"|Payload size (lower 8 bits)
 +
|-
 +
! 0x04
 +
| colspan="8"|Payload byte 0
 +
|-
 +
| colspan="9"|...
 
|-
 
|-
| 0xA5
+
! 0x04+n-1
| 0x00
+
| colspan="8"|Payload byte n-1
| (size >> 8) <nowiki>|</nowiki> 0x40
+
|-
| size & 0xFF
+
! 0x04+n
 +
| colspan="8"|[[CRC-8-CCITT]] computer over whole packet
 
|}
 
|}
 +
 +
=== Header ===
 +
The packet header is fixed and consists in a synchronization byte (0xA5), followed by a unused (possibly RFU) zero byte. After these two hardcoded bytes, there's a varint representing the payload size, which may use one byte or two, depending on the how big the payload is.
  
 
In C:
 
In C:
Line 87: Line 143:
 
}</nowiki>
 
}</nowiki>
  
== Payload ==
+
=== Payload ===
 
The payload is obfuscated using a XOR-based encryption. In C:
 
The payload is obfuscated using a XOR-based encryption. In C:
 
  <nowiki>void payloadObfuscate(const void * voidplain, void * voidcipher, size_t size) {
 
  <nowiki>void payloadObfuscate(const void * voidplain, void * voidcipher, size_t size) {
 
uint16_t * plain = (uint16_t *) voidplain;
 
uint16_t * plain = (uint16_t *) voidplain;
 
uint16_t * cipher = (uint16_t *) voidcipher;
 
uint16_t * cipher = (uint16_t *) voidcipher;
size_t halfCount = size / sizeof(uint16_t);
+
size_t halfCount = size >> 1; // Divide by 2 rounding towards zero
  
 
uint16_t xorval = htobe16(0xE963);
 
uint16_t xorval = htobe16(0xE963);
Line 106: Line 162:
 
uint16_t * cipher = (uint16_t *) voidcipher;
 
uint16_t * cipher = (uint16_t *) voidcipher;
 
uint16_t * plain = (uint16_t *) voidplain;
 
uint16_t * plain = (uint16_t *) voidplain;
size_t halfCount = size / sizeof(uint16_t);
+
size_t halfCount = size >> 1; // Divide by 2 rounding towards zero
  
if (halfCount) {
+
uint16_t xorval = htobe16(0xE963);
size_t i;
+
size_t i;
for (i = halfCount - 1; i > 0; i--) {
 
plain[i] = cipher[i] ^ cipher[i - 1];
 
}
 
  
plain[0] = cipher[0] ^ htobe16(0xE963);
+
for (i = 0; i < halfCount; i++) {
 +
uint16_t word = plain[i];
 +
cipher[i] = xorval ^ word;
 +
xorval = word;
 
}
 
}
 
}</nowiki>
 
}</nowiki>
  
== Error detection ==
+
=== Error detection ===
 
The trailing error detection byte is calculated using [[CRC-8-CCITT]] <b>over the whole packet</b> (both the header and the payload)
 
The trailing error detection byte is calculated using [[CRC-8-CCITT]] <b>over the whole packet</b> (both the header and the payload)
  
 +
== Layer 2 - "ircom" ==
 +
ircom is a simple stateful point-to-point master-slave communication protocol built on top of IR layer 1.
 +
 +
{| class="wikitable" style="margin: 0 auto; text-align: center;"
 +
! style="border-top: none" | Byte
 +
! style="width:10%;"| 7
 +
! style="width:10%;"| 6
 +
! style="width:10%;"| 5
 +
! style="width:10%;"| 4
 +
! style="width:10%;"| 3
 +
! style="width:10%;"| 2
 +
! style="width:10%;"| 1
 +
! style="width:10%;"| 0
 +
|-
 +
! 0x00
 +
| colspan="8"|Random value in range <code>0x01</code>~<code>0xFE</code>
 +
|-
 +
! 0x01
 +
| colspan="8"|Random value in range <code>0x01</code>~<code>0xFE</code>
 +
|-
 +
! 0x02
 +
| colspan="8"|Random value in range <code>0x01</code>~<code>0xFE</code>
 +
|-
 +
! 0x03
 +
| colspan="8"|Random value in range <code>0x01</code>~<code>0xFE</code>
 +
|-
 +
! 0x04
 +
| colspan="4"|RFU?
 +
| colspan="4"|Protocol version: <code>0x1</code>
 +
|-
 +
! 0x05
 +
| colspan="8"|Connection identifier of master (3DS), value determined by master
 +
|-
 +
! 0x06
 +
| colspan="8"|Connection identifier of slave (NFC adapter), value determined by slave
 +
|-
 +
! 0x07
 +
| colspan="4"|???
 +
| colspan="4"|Operation code
 +
|-
 +
! 0x08+
 +
| colspan="8" style="background:#ffd0d0;"|Payload (if any)
 +
|}
 +
 +
* Random values are generated using a Mersenne Twister whose seed is based off the NFC adapter system tick counter. It is therefore random, and the 3DS won't attempt to validate them by any means. Its purpose is to make every encrypted packet look completely different to the previous one. This makes a replay attack impossible without knowing the encryption algorithm.
 +
* NFC adapter will ignore packets whose protocol version is not 1. It will not even reply.
 +
* Connection identifier is a random byte the 3DS assigns to identify the connection should there be several connections in range at once. Slave devices must save this value from the initial handshake packet and use it for replies. The 3DS will also save the connection identifier byte of the slave which is also random. The 3DS must also ignore packets whose connection ID does not match.
 +
 +
=== Operation codes ===
 +
{| class="wikitable"
 +
! Code
 +
! Name
 +
! Has payload
 +
! Direction
 +
|-
 +
! 0x0
 +
| Layer 3 command
 +
| style="background: green" | Yes
 +
| Master to slave
 +
|-
 +
! 0x1
 +
| ???
 +
| ???
 +
|-
 +
! 0x2
 +
| ???
 +
| ???
 +
|-
 +
! 0x3
 +
| ???
 +
| ???
 +
|-
 +
! 0x4
 +
| ???
 +
| ???
 +
|-
 +
! 0x5
 +
| ???
 +
| ???
 +
|-
 +
! 0x6
 +
| ???
 +
| ???
 +
|-
 +
! 0x7
 +
| ???
 +
| ???
 +
|-
 +
! 0x8
 +
| ???
 +
| ???
 +
|-
 +
! 0x9
 +
| ???
 +
| ???
 +
|-
 +
! 0xA
 +
| Disconnect request
 +
| style="background: red" | No
 +
| Master to slave
 +
|-
 +
! 0xB
 +
| Disconnection acknowledgment
 +
| style="background: red" | No
 +
| Slave to master
 +
|-
 +
! 0xC
 +
| Handshake
 +
| style="background: red" | No
 +
| Master to slave
 +
|-
 +
! 0xD
 +
| Handshake acknowledgement
 +
| style="background: red" | No
 +
| Slave to master
 +
|-
 +
! 0xE
 +
| ???
 +
| ???
 +
|-
 +
! 0xF
 +
| ???
 +
| ???
 +
|}
 +
 +
== Layer 3 ==
 +
Layer 3 is the payload of layer 2. A lot is unknown of this layer and thus a lot of assumptions were made in the following tables.
 +
 +
Layer 3 contains the following data:
 +
{| class="wikitable" style="margin: 0 auto; text-align: center;"
 +
! style="border-top: none" | Byte
 +
! style="width:10%;"| 7
 +
! style="width:10%;"| 6
 +
! style="width:10%;"| 5
 +
! style="width:10%;"| 4
 +
! style="width:10%;"| 3
 +
! style="width:10%;"| 2
 +
! style="width:10%;"| 1
 +
! style="width:10%;"| 0
 +
|-
 +
! 0x00
 +
| colspan="4"|Request identifier nibble
 +
| colspan="4"|Always <code>0x1</code>
 +
|-
 +
! 0x01
 +
| colspan="8"|Slave/master identifier byte
 +
|-
 +
! 0x02
 +
| colspan="8"|Request type code upper byte
 +
|-
 +
! 0x03
 +
| colspan="8"|Request type code lower byte
 +
|-
 +
! 0x04+
 +
| colspan="8" style="background:#ffd0d0;"|Payload (if any)
 +
|}
 +
*Request identifier nibble is incremented by <code>0x1</code> at every new request by the master, the same value for this byte is also sent by the slave in response to the request of the master
 +
*Slave/master identifier byte is <code>0x1</code> for a message from the master and <code>0x10</code> for a message from the slave
 +
 +
=== Request type codes ===
 +
{| class="wikitable"
 +
! Code
 +
! Request description
 +
! Direction
 +
! Has payload
 +
|-
 +
! 0x0000
 +
| ACK
 +
| Slave to master
 +
| style="background: green" | Yes
 +
|-
 +
! 0x0003
 +
| Get firmware version and battery level of NFC adapter
 +
| Master to slave
 +
| style="background: red" | No
 +
|-
 +
! 0x0004
 +
| Firmware version and battery level
 +
| Slave to master
 +
| style="background: green" | Yes
 +
|-
 +
! 0x0100
 +
| Unknown, slave always responds with ACK
 +
| Master to slave
 +
| style="background: green" | Yes
 +
|-
 +
! 0x0202
 +
| Request to stop communication with NFC tag
 +
| Master to slave
 +
| style="background: red" | No
 +
|-
 +
! 0x0204
 +
| Get dumped data from NFC tag
 +
| Master to slave
 +
| style="background: red" | No
 +
|-
 +
! 0x0205
 +
| Data from NFC tag
 +
| Slave to master
 +
| style="background: green" | Yes
 +
|-
 +
! 0x0206
 +
| Request to start communication with NFC tag
 +
| Master to slave
 +
| style="background: green" | Yes
 +
|-
 +
! 0x0207
 +
| Request to write data to NFC tag
 +
| Master to slave
 +
| style="background: green" | Yes
 +
|}
 +
=== Payload ===
 +
==== 0x0000 ====
 +
Acknowledgement message always send by slave. Payload always contains <code>0x000000AA</code>
 +
==== 0x0004 ====
 +
Payload contains firmware version and battery level of NFC adapter. Payload has a size of 6 bytes.
 +
{| class="wikitable" style="margin: 0 auto; text-align: center;"
 +
! style="border-top: none" | Byte
 +
! style="width:10%;"| 7
 +
! style="width:10%;"| 6
 +
! style="width:10%;"| 5
 +
! style="width:10%;"| 4
 +
! style="width:10%;"| 3
 +
! style="width:10%;"| 2
 +
! style="width:10%;"| 1
 +
! style="width:10%;"| 0
 +
|-
 +
! 0x00
 +
| colspan="8"|Upper or lower byte of version number, newest firmware is <code>0x1</code>
 +
|-
 +
! 0x01
 +
| colspan="8"|Upper or lower byte of version number, newest firmware is <code>0x6</code>
 +
|-
 +
! 0x02
 +
| colspan="8"|Padding byte? Always <code>0x0</code>
 +
|-
 +
! 0x03
 +
| colspan="8"|Padding byte? Always <code>0x0</code>
 +
|-
 +
! 0x04
 +
| colspan="8"|Battery level. Values varies between <code>0x3</code> for full battery and <code>0x0</code> for empty
 +
|-
 +
! 0x05
 +
| colspan="8"|Padding byte? Always <code>0x0</code>
 +
|}
 +
*NFC reader LED already turns red when the battery level byte is <code>0x2</code>, this will also trigger a low battery level warning on the 3DS
 +
==== 0x0100 ====
 +
The purpose of this request by the master is unknown. Slave always responds with ACK. Payload of this request is always <code>0x0003E8AA</code>
 +
==== 0x0205 ====
 +
Payload contains data regarding NFC communication. The first byte of the payload means the following:
 +
{| class="wikitable"
 +
! style="border-top: none" | Code
 +
! Description
 +
|-
 +
! 0x00
 +
| colspan="8"|NFC communication is stopped as a result of a <code>0x0202</code> request by the master
 +
|-
 +
! 0x01
 +
| colspan="8"|No NFC tag on top of the reader
 +
|-
 +
! 0x02
 +
| colspan="8"|Busy dumping NFC tag
 +
|-
 +
! 0x03
 +
| colspan="8"|NFC tag dump after write by master
 +
|-
 +
! 0x04
 +
| colspan="8"|NFC tag fully dumped
 +
|-
 +
! 0x05
 +
| colspan="8"|NFC tag dump after write by master
 +
|-
 +
! 0x07
 +
| colspan="8"|NFC tag not a NTAG215 or contains no Amiibo compatible data
 +
|-
 +
! 0x08
 +
| colspan="8"|NFC tag removed from reader
 +
|}
 +
*After the tag is written by the master and dumped again, the first few dumps start with 0x03, this changes to 0x05 after a few dumps. The reason for this is unknown
 +
==== 0x0206 ====
 +
Request from master to start NFC communication. Payload always contains 19 <code>0x0</code> padding bytes, followed by <code>0xC80300393A737486000001</code> and another 26 <code>0x0</code> padding bytes.
 +
==== 0x0207 ====
 +
Request from master to write to NFC tag. The request packet already contains the desired data to be written to the tag. Payload start with two <code>0x00</code> padding bytes followed by the 7 ID bytes of the tag. These ID bytes are used by the NFC adapter to check if same Amiibo is placed on the NFC adapter again.
 
== Samples ==
 
== Samples ==
  
NFC adapter update beacon, as sent by the console:
+
NFC handshake beacons:
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! Raw
+
! Layer 1 packet
! Deobfuscated
+
! Layer 2 packet
 +
! Layer 3 packet
 
|-
 
|-
 
| <code>A5 00 08 73 FE A5 C4 A4 2C A4 20 F5</code>
 
| <code>A5 00 08 73 FE A5 C4 A4 2C A4 20 F5</code>
 
| <code>9A 9D D6 3A 01 E8 00 0C</code>
 
| <code>9A 9D D6 3A 01 E8 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 D1 3E B7 7B B6 91 B6 9D 87</code>
 
| <code>A5 00 08 D1 3E B7 7B B6 91 B6 9D 87</code>
 
| <code>38 5D 66 45 01 EA 00 0C</code>
 
| <code>38 5D 66 45 01 EA 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 09 58 23 36 22 DA 22 D6 AE</code>
 
| <code>A5 00 08 09 58 23 36 22 DA 22 D6 AE</code>
 
| <code>E0 3B 2A 6E 01 EC 00 0C</code>
 
| <code>E0 3B 2A 6E 01 EC 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 5E DD A4 A0 A5 4E A5 42 A8</code>
 
| <code>A5 00 08 5E DD A4 A0 A5 4E A5 42 A8</code>
 
| <code>B7 BE FA 7D 01 EE 00 0C</code>
 
| <code>B7 BE FA 7D 01 EE 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 BC 19 C6 37 C7 C7 C7 CB 8B</code>
 
| <code>A5 00 08 BC 19 C6 37 C7 C7 C7 CB 8B</code>
 
| <code>55 7A 7A 2E 01 F0 00 0C</code>
 
| <code>55 7A 7A 2E 01 F0 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 C9 15 F6 63 F7 91 F7 9D B2</code>
 
| <code>A5 00 08 C9 15 F6 63 F7 91 F7 9D B2</code>
 
| <code>20 76 3F 76 01 F2 00 0C</code>
 
| <code>20 76 3F 76 01 F2 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 6E 48 47 1A 46 EE 46 E2 C7</code>
 
| <code>A5 00 08 6E 48 47 1A 46 EE 46 E2 C7</code>
 
| <code>87 2B 29 52 01 F4 00 0C</code>
 
| <code>87 2B 29 52 01 F4 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 A2 8C E5 C3 E4 35 E4 39 74</code>
 
| <code>A5 00 08 A2 8C E5 C3 E4 35 E4 39 74</code>
 
| <code>4B EF 47 4F 01 F6 00 0C</code>
 
| <code>4B EF 47 4F 01 F6 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 26 1C 07 10 06 E8 06 E4 64</code>
 
| <code>A5 00 08 26 1C 07 10 06 E8 06 E4 64</code>
 
| <code>CF 7F 21 0C 01 F8 00 0C</code>
 
| <code>CF 7F 21 0C 01 F8 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 7E 73 A2 3F A3 C5 A3 C9 FD</code>
 
| <code>A5 00 08 7E 73 A2 3F A3 C5 A3 C9 FD</code>
 
| <code>97 10 DC 4C 01 FA 00 0C</code>
 
| <code>97 10 DC 4C 01 FA 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 75 00 F3 B8 F2 44 F2 48 63</code>
 
| <code>A5 00 08 75 00 F3 B8 F2 44 F2 48 63</code>
 
| <code>9C 63 86 B8 01 FC 00 0C</code>
 
| <code>9C 63 86 B8 01 FC 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 8D AC 0F D5 0E 2B 0E 27 72</code>
 
| <code>A5 00 08 8D AC 0F D5 0E 2B 0E 27 72</code>
 
| <code>64 CF 82 79 01 FE 00 0C</code>
 
| <code>64 CF 82 79 01 FE 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 A3 55 7C 53 7D 52 7D 5E B2</code>
 
| <code>A5 00 08 A3 55 7C 53 7D 52 7D 5E B2</code>
 
| <code>4A 36 DF 06 01 01 00 0C</code>
 
| <code>4A 36 DF 06 01 01 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 15 06 43 C0 42 C3 42 CF 85</code>
 
| <code>A5 00 08 15 06 43 C0 42 C3 42 CF 85</code>
 
| <code>FC 65 56 C6 01 03 00 0C</code>
 
| <code>FC 65 56 C6 01 03 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 66 E0 9A 17 9B 12 9B 1E A0</code>
 
| <code>A5 00 08 66 E0 9A 17 9B 12 9B 1E A0</code>
 
| <code>8F 83 FC F7 01 05 00 0C</code>
 
| <code>8F 83 FC F7 01 05 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 A4 35 09 97 08 90 08 9C 25</code>
 
| <code>A5 00 08 A4 35 09 97 08 90 08 9C 25</code>
 
| <code>4D 56 AD A2 01 07 00 0C</code>
 
| <code>4D 56 AD A2 01 07 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 73 E2 BD AF BC A6 BC AA 60</code>
 
| <code>A5 00 08 73 E2 BD AF BC A6 BC AA 60</code>
 
| <code>9A 81 CE 4D 01 09 00 0C</code>
 
| <code>9A 81 CE 4D 01 09 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 02 57 D7 B0 D6 BB D6 B7 28</code>
 
| <code>A5 00 08 02 57 D7 B0 D6 BB D6 B7 28</code>
 
| <code>EB 34 D5 E7 01 0B 00 0C</code>
 
| <code>EB 34 D5 E7 01 0B 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 0D 79 01 AA 00 A7 00 AB 22</code>
 
| <code>A5 00 08 0D 79 01 AA 00 A7 00 AB 22</code>
 
| <code>E4 1A 0C D3 01 0D 00 0C</code>
 
| <code>E4 1A 0C D3 01 0D 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 14 91 04 B9 05 B6 05 BA B2</code>
 
| <code>A5 00 08 14 91 04 B9 05 B6 05 BA B2</code>
 
| <code>FD F2 10 28 01 0F 00 0C</code>
 
| <code>FD F2 10 28 01 0F 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 2C 86 B1 49 B0 58 B0 54 C0</code>
 
| <code>A5 00 08 2C 86 B1 49 B0 58 B0 54 C0</code>
 
| <code>C5 E5 9D CF 01 11 00 0C</code>
 
| <code>C5 E5 9D CF 01 11 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 D5 1D DE DB DF C8 DF C4 F9</code>
 
| <code>A5 00 08 D5 1D DE DB DF C8 DF C4 F9</code>
 
| <code>3C 7E 0B C6 01 13 00 0C</code>
 
| <code>3C 7E 0B C6 01 13 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 AF 75 DE 5C DF 49 DF 45 9C</code>
 
| <code>A5 00 08 AF 75 DE 5C DF 49 DF 45 9C</code>
 
| <code>46 16 71 29 01 15 00 0C</code>
 
| <code>46 16 71 29 01 15 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 C8 E2 5B C6 5A D1 5A DD B5</code>
 
| <code>A5 00 08 C8 E2 5B C6 5A D1 5A DD B5</code>
 
| <code>21 81 93 24 01 17 00 0C</code>
 
| <code>21 81 93 24 01 17 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 9B 51 68 2D 69 34 69 38 41</code>
 
| <code>A5 00 08 9B 51 68 2D 69 34 69 38 41</code>
 
| <code>72 32 F3 7C 01 19 00 0C</code>
 
| <code>72 32 F3 7C 01 19 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 13 7B 9F EF 9E F4 9E F8 32</code>
 
| <code>A5 00 08 13 7B 9F EF 9E F4 9E F8 32</code>
 
| <code>FA 18 8C 94 01 1B 00 0C</code>
 
| <code>FA 18 8C 94 01 1B 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 A7 62 02 9C 03 81 03 8D BD</code>
 
| <code>A5 00 08 A7 62 02 9C 03 81 03 8D BD</code>
 
| <code>4E 01 A5 FE 01 1D 00 0C</code>
 
| <code>4E 01 A5 FE 01 1D 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 39 06 94 36 95 29 95 25 09</code>
 
| <code>A5 00 08 39 06 94 36 95 29 95 25 09</code>
 
| <code>D0 65 AD 30 01 1F 00 0C</code>
 
| <code>D0 65 AD 30 01 1F 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 32 4C D7 C0 D6 E1 D6 ED 92</code>
 
| <code>A5 00 08 32 4C D7 C0 D6 E1 D6 ED 92</code>
 
| <code>DB 2F E5 8C 01 21 00 0C</code>
 
| <code>DB 2F E5 8C 01 21 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 83 BE F2 8F F3 AC F3 A0 B1</code>
 
| <code>A5 00 08 83 BE F2 8F F3 AC F3 A0 B1</code>
 
| <code>6A DD 71 31 01 23 00 0C</code>
 
| <code>6A DD 71 31 01 23 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 83 5E A0 57 A1 72 A1 7E F0</code>
 
| <code>A5 00 08 83 5E A0 57 A1 72 A1 7E F0</code>
 
| <code>6A 3D 23 09 01 25 00 0C</code>
 
| <code>6A 3D 23 09 01 25 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 6E C8 AD 69 AC 4E AC 42 D1</code>
 
| <code>A5 00 08 6E C8 AD 69 AC 4E AC 42 D1</code>
 
| <code>87 AB C3 A1 01 27 00 0C</code>
 
| <code>87 AB C3 A1 01 27 00 0C</code>
 +
| ␀
 
|-
 
|-
 
| <code>A5 00 08 C7 33 A1 2C A0 05 A0 09 FC</code>
 
| <code>A5 00 08 C7 33 A1 2C A0 05 A0 09 FC</code>
 
| <code>2E 50 66 1F 01 29 00 0C</code>
 
| <code>2E 50 66 1F 01 29 00 0C</code>
 +
| ␀
 
|}
 
|}
  
 
= External links =
 
= External links =
 
* [https://www.broadcom.com/products/NFC/NFC-Solutions/BCM2079x-Family BCM2079x brief on Broadcom's website]
 
* [https://www.broadcom.com/products/NFC/NFC-Solutions/BCM2079x-Family BCM2079x brief on Broadcom's website]
 +
* [https://github.com/HubSteven/3ds_ir/ Python scripts to sniff and spoof IR communication between the NFC reader and 3DS using an IRda adapter]

Latest revision as of 22:35, 17 October 2019

The Nintendo NFC adapter, formally Nintendo NFC Reader/Writer and codenamed Fangate, is an external device which adds NFC capabilities for amiibos to old Nintendo 3DS and Nintendo 2DS consoles, using the infrared port on the back of the console.

It launched simultaneously with Animal Crossing Happy Home Designer, with which it's optionally bundled; it can also be bought standalone at a nominal price of 21 €.

Media preview, as released by Nintendo


Technical details[edit]

Based on analysis of the fangate_updater.bin file, which is part of the old Nintendo 3DS operating system since 9.3.0-21 and contains the firmware running on the external adapter; and analysis of the NFC Services running on old 3DS.

  • SOC inside the adapter: Broadcom BCM20791B1 or ST proprietary "MCU-FGT/rev.A/GH24S VQ"
  • CPU: ARM Cortex M0
  • Communications: infrared, with ir:USER running on the console. Uses obfuscated payloads. Baud rate is 115200 bps.

Memory map:

Address Size Description
0x08008000 256KB? Firmware (fangate_updater.bin)
0x20000000 128KB? RAM
0x40023C00 0x1C FLASH ROM control
0xE000ED00 0x104 ARM Cortex system control block

IR communications[edit]

Packets are sent using IrDA-SIR (using ir:USER), with a 8N1 encoding (eight data bits, one stop bit, without parity). Each one is formed by a 2-byte header, a varint with the payload size, an obfuscated payload, and trailing error detection byte.

Layer 1 - framing format[edit]

Frames are encoded using two different yet very simmilar formats, depending on how large the payload to be transmitted is:

  • For payloads with less than 64 bytes, the third byte represents the payload size.
  • For packets with up to 16383 bytes, the size is split in two bytes, with the third byte being the upper 6 bits of the payload size, OR'd with 0x40, and the fourth being the lower eight bits of the payload size
IR framing format - short frame
Byte 7 6 5 4 3 2 1 0
0x00 Synchronization byte (0xA5)
0x01 Reserved for future use (0x00)
0x02 RFU (0) Short frame (0) Payload size
0x03 Payload byte 0
...
0x03+n-1 Payload byte n-1
0x03+n CRC-8-CCITT computer over whole packet


IR framing format - long frame
Byte 7 6 5 4 3 2 1 0
0x00 Synchronization byte: 0xA5
0x01 Reserved for future use
0x02 RFU (0) Long frame (1) Payload size (upper 6 bits)
0x03 Payload size (lower 8 bits)
0x04 Payload byte 0
...
0x04+n-1 Payload byte n-1
0x04+n CRC-8-CCITT computer over whole packet

Header[edit]

The packet header is fixed and consists in a synchronization byte (0xA5), followed by a unused (possibly RFU) zero byte. After these two hardcoded bytes, there's a varint representing the payload size, which may use one byte or two, depending on the how big the payload is.

In C:

uint8_t * setPacketHeader(uint8_t * buffer, size_t payloadSize) {
	assert(payloadSize < 16384);

	buffer[0] = 0xA5;
	buffer[1] = 0x00;

	if (payloadSize < 64) {
		buffer[2] = payloadSize;
		buffer += 3;
	} else {
		buffer[2] = 0x40 | (payloadSize >> 8);
		buffer[3] = payloadSize;
		buffer += 4;
	}

	return buffer;
}

Payload[edit]

The payload is obfuscated using a XOR-based encryption. In C:

void payloadObfuscate(const void * voidplain, void * voidcipher, size_t size) {
	uint16_t * plain = (uint16_t *) voidplain;
	uint16_t * cipher = (uint16_t *) voidcipher;
	size_t halfCount = size >> 1; // Divide by 2 rounding towards zero

	uint16_t xorval = htobe16(0xE963);
	size_t i;

	for (i = 0; i < halfCount; i++) {
		xorval ^= plain[i];
		cipher[i] = xorval;
	}
}

void payloadDeobfuscate(const void * voidcipher, void * voidplain, size_t size) {
	uint16_t * cipher = (uint16_t *) voidcipher;
	uint16_t * plain = (uint16_t *) voidplain;
	size_t halfCount = size >> 1; // Divide by 2 rounding towards zero

	uint16_t xorval = htobe16(0xE963);
	size_t i;

	for (i = 0; i < halfCount; i++) {
		uint16_t word = plain[i];
		cipher[i] = xorval ^ word;
		xorval = word;
	}
}

Error detection[edit]

The trailing error detection byte is calculated using CRC-8-CCITT over the whole packet (both the header and the payload)

Layer 2 - "ircom"[edit]

ircom is a simple stateful point-to-point master-slave communication protocol built on top of IR layer 1.

Byte 7 6 5 4 3 2 1 0
0x00 Random value in range 0x01~0xFE
0x01 Random value in range 0x01~0xFE
0x02 Random value in range 0x01~0xFE
0x03 Random value in range 0x01~0xFE
0x04 RFU? Protocol version: 0x1
0x05 Connection identifier of master (3DS), value determined by master
0x06 Connection identifier of slave (NFC adapter), value determined by slave
0x07 ??? Operation code
0x08+ Payload (if any)
  • Random values are generated using a Mersenne Twister whose seed is based off the NFC adapter system tick counter. It is therefore random, and the 3DS won't attempt to validate them by any means. Its purpose is to make every encrypted packet look completely different to the previous one. This makes a replay attack impossible without knowing the encryption algorithm.
  • NFC adapter will ignore packets whose protocol version is not 1. It will not even reply.
  • Connection identifier is a random byte the 3DS assigns to identify the connection should there be several connections in range at once. Slave devices must save this value from the initial handshake packet and use it for replies. The 3DS will also save the connection identifier byte of the slave which is also random. The 3DS must also ignore packets whose connection ID does not match.

Operation codes[edit]

Code Name Has payload Direction
0x0 Layer 3 command Yes Master to slave
0x1 ??? ???
0x2 ??? ???
0x3 ??? ???
0x4 ??? ???
0x5 ??? ???
0x6 ??? ???
0x7 ??? ???
0x8 ??? ???
0x9 ??? ???
0xA Disconnect request No Master to slave
0xB Disconnection acknowledgment No Slave to master
0xC Handshake No Master to slave
0xD Handshake acknowledgement No Slave to master
0xE ??? ???
0xF ??? ???

Layer 3[edit]

Layer 3 is the payload of layer 2. A lot is unknown of this layer and thus a lot of assumptions were made in the following tables.

Layer 3 contains the following data:

Byte 7 6 5 4 3 2 1 0
0x00 Request identifier nibble Always 0x1
0x01 Slave/master identifier byte
0x02 Request type code upper byte
0x03 Request type code lower byte
0x04+ Payload (if any)
  • Request identifier nibble is incremented by 0x1 at every new request by the master, the same value for this byte is also sent by the slave in response to the request of the master
  • Slave/master identifier byte is 0x1 for a message from the master and 0x10 for a message from the slave

Request type codes[edit]

Code Request description Direction Has payload
0x0000 ACK Slave to master Yes
0x0003 Get firmware version and battery level of NFC adapter Master to slave No
0x0004 Firmware version and battery level Slave to master Yes
0x0100 Unknown, slave always responds with ACK Master to slave Yes
0x0202 Request to stop communication with NFC tag Master to slave No
0x0204 Get dumped data from NFC tag Master to slave No
0x0205 Data from NFC tag Slave to master Yes
0x0206 Request to start communication with NFC tag Master to slave Yes
0x0207 Request to write data to NFC tag Master to slave Yes

Payload[edit]

0x0000[edit]

Acknowledgement message always send by slave. Payload always contains 0x000000AA

0x0004[edit]

Payload contains firmware version and battery level of NFC adapter. Payload has a size of 6 bytes.

Byte 7 6 5 4 3 2 1 0
0x00 Upper or lower byte of version number, newest firmware is 0x1
0x01 Upper or lower byte of version number, newest firmware is 0x6
0x02 Padding byte? Always 0x0
0x03 Padding byte? Always 0x0
0x04 Battery level. Values varies between 0x3 for full battery and 0x0 for empty
0x05 Padding byte? Always 0x0
  • NFC reader LED already turns red when the battery level byte is 0x2, this will also trigger a low battery level warning on the 3DS

0x0100[edit]

The purpose of this request by the master is unknown. Slave always responds with ACK. Payload of this request is always 0x0003E8AA

0x0205[edit]

Payload contains data regarding NFC communication. The first byte of the payload means the following:

Code Description
0x00 NFC communication is stopped as a result of a 0x0202 request by the master
0x01 No NFC tag on top of the reader
0x02 Busy dumping NFC tag
0x03 NFC tag dump after write by master
0x04 NFC tag fully dumped
0x05 NFC tag dump after write by master
0x07 NFC tag not a NTAG215 or contains no Amiibo compatible data
0x08 NFC tag removed from reader
  • After the tag is written by the master and dumped again, the first few dumps start with 0x03, this changes to 0x05 after a few dumps. The reason for this is unknown

0x0206[edit]

Request from master to start NFC communication. Payload always contains 19 0x0 padding bytes, followed by 0xC80300393A737486000001 and another 26 0x0 padding bytes.

0x0207[edit]

Request from master to write to NFC tag. The request packet already contains the desired data to be written to the tag. Payload start with two 0x00 padding bytes followed by the 7 ID bytes of the tag. These ID bytes are used by the NFC adapter to check if same Amiibo is placed on the NFC adapter again.

Samples[edit]

NFC handshake beacons:

Layer 1 packet Layer 2 packet Layer 3 packet
A5 00 08 73 FE A5 C4 A4 2C A4 20 F5 9A 9D D6 3A 01 E8 00 0C
A5 00 08 D1 3E B7 7B B6 91 B6 9D 87 38 5D 66 45 01 EA 00 0C
A5 00 08 09 58 23 36 22 DA 22 D6 AE E0 3B 2A 6E 01 EC 00 0C
A5 00 08 5E DD A4 A0 A5 4E A5 42 A8 B7 BE FA 7D 01 EE 00 0C
A5 00 08 BC 19 C6 37 C7 C7 C7 CB 8B 55 7A 7A 2E 01 F0 00 0C
A5 00 08 C9 15 F6 63 F7 91 F7 9D B2 20 76 3F 76 01 F2 00 0C
A5 00 08 6E 48 47 1A 46 EE 46 E2 C7 87 2B 29 52 01 F4 00 0C
A5 00 08 A2 8C E5 C3 E4 35 E4 39 74 4B EF 47 4F 01 F6 00 0C
A5 00 08 26 1C 07 10 06 E8 06 E4 64 CF 7F 21 0C 01 F8 00 0C
A5 00 08 7E 73 A2 3F A3 C5 A3 C9 FD 97 10 DC 4C 01 FA 00 0C
A5 00 08 75 00 F3 B8 F2 44 F2 48 63 9C 63 86 B8 01 FC 00 0C
A5 00 08 8D AC 0F D5 0E 2B 0E 27 72 64 CF 82 79 01 FE 00 0C
A5 00 08 A3 55 7C 53 7D 52 7D 5E B2 4A 36 DF 06 01 01 00 0C
A5 00 08 15 06 43 C0 42 C3 42 CF 85 FC 65 56 C6 01 03 00 0C
A5 00 08 66 E0 9A 17 9B 12 9B 1E A0 8F 83 FC F7 01 05 00 0C
A5 00 08 A4 35 09 97 08 90 08 9C 25 4D 56 AD A2 01 07 00 0C
A5 00 08 73 E2 BD AF BC A6 BC AA 60 9A 81 CE 4D 01 09 00 0C
A5 00 08 02 57 D7 B0 D6 BB D6 B7 28 EB 34 D5 E7 01 0B 00 0C
A5 00 08 0D 79 01 AA 00 A7 00 AB 22 E4 1A 0C D3 01 0D 00 0C
A5 00 08 14 91 04 B9 05 B6 05 BA B2 FD F2 10 28 01 0F 00 0C
A5 00 08 2C 86 B1 49 B0 58 B0 54 C0 C5 E5 9D CF 01 11 00 0C
A5 00 08 D5 1D DE DB DF C8 DF C4 F9 3C 7E 0B C6 01 13 00 0C
A5 00 08 AF 75 DE 5C DF 49 DF 45 9C 46 16 71 29 01 15 00 0C
A5 00 08 C8 E2 5B C6 5A D1 5A DD B5 21 81 93 24 01 17 00 0C
A5 00 08 9B 51 68 2D 69 34 69 38 41 72 32 F3 7C 01 19 00 0C
A5 00 08 13 7B 9F EF 9E F4 9E F8 32 FA 18 8C 94 01 1B 00 0C
A5 00 08 A7 62 02 9C 03 81 03 8D BD 4E 01 A5 FE 01 1D 00 0C
A5 00 08 39 06 94 36 95 29 95 25 09 D0 65 AD 30 01 1F 00 0C
A5 00 08 32 4C D7 C0 D6 E1 D6 ED 92 DB 2F E5 8C 01 21 00 0C
A5 00 08 83 BE F2 8F F3 AC F3 A0 B1 6A DD 71 31 01 23 00 0C
A5 00 08 83 5E A0 57 A1 72 A1 7E F0 6A 3D 23 09 01 25 00 0C
A5 00 08 6E C8 AD 69 AC 4E AC 42 D1 87 AB C3 A1 01 27 00 0C
A5 00 08 C7 33 A1 2C A0 05 A0 09 FC 2E 50 66 1F 01 29 00 0C

External links[edit]