Difference between revisions of "RCD"
(Created page with "RCD is a framework that manages '''r'''emote '''c'''ontrolled '''d'''evices - RC toys that connect to the Nintendo Switch via 802.11 ("WiFi") link. The first (and currently on...") |
(Document RCD handshake) |
||
Line 34: | Line 34: | ||
In theory, multiple "services" could be multiplexed over a single TCP connection, but in practice only a single service is allowed per TCP port. | In theory, multiple "services" could be multiplexed over a single TCP connection, but in practice only a single service is allowed per TCP port. | ||
− | + | = Handshake protocol = | |
− | Service ID: 0x0001 | + | Service ID: 0x0001<br /> |
Commands: 4 | Commands: 4 | ||
− | When a device first establishes a wireless connection, it connects to the Switch on a well-known port to access the "handshake" service and make its presence known. A successful handshake causes application-specific RCD connections to be established. Commands must be sent in order (and no more than once). | + | When a device first establishes a wireless connection, it connects to the Switch on a well-known port to access the "handshake" service and make its presence known. A successful handshake causes application-specific RCD connections to be established. Commands must be sent in order (and no more than once), or error code 0x810e8 is generated by the host. |
+ | |||
+ | == Command 0x1: Begin handshake == | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |+ Command 0x1 input+output payload | ||
+ | |- | ||
+ | ! Offset | ||
+ | ! Size | ||
+ | ! Description | ||
+ | |- | ||
+ | | 0x0 || 0x10 || Handshake protocol version; one byte followed by zero-padding. Must be 1. | ||
+ | |- | ||
+ | | 0x10 || 0x10 || Device "name" (e.g. 'Fuji'), followed by zero-padding. The Switch uses the empty string. | ||
+ | |- | ||
+ | | 0x20 || 0x10 || Device identifier. Devices embed their MAC as the last 6 bytes; host seems to use a randomly-generated UUID. | ||
+ | |- | ||
+ | | 0x30 || 0x20 || Nonce; cryptographic random bytes with no special purpose. | ||
+ | |} | ||
+ | |||
+ | This command is sent first (by the device). The host responds in kind. Error code 0x800e8 is generated if the handshake version is not 1. | ||
+ | |||
+ | == Command 0x2: Version negotiation == | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |+ Command 0x2 input payload | ||
+ | |- | ||
+ | ! Offset | ||
+ | ! Size | ||
+ | ! Description | ||
+ | |- | ||
+ | | 0x0 || 0x20 || Pairing identifier | ||
+ | |- | ||
+ | | 0x20 || 0x1 || Number of versions offered | ||
+ | |- | ||
+ | | 0x21 || Varies || One 8-bit integer per version offered. There is no padding. | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |+ Command 0x2 output payload | ||
+ | |- | ||
+ | ! Offset | ||
+ | ! Size | ||
+ | ! Description | ||
+ | |- | ||
+ | | 0x0 || 0x20 || Pairing identifier | ||
+ | |- | ||
+ | | 0x20 || 0x1 || Selected version | ||
+ | |- | ||
+ | | 0x21 || 0xF || Zero-padding | ||
+ | |} | ||
+ | |||
+ | There are a few moving parts here. | ||
+ | |||
+ | Firstly, this command is responsible for negotiating a version for the underlying protocols. The host looks at all versions offered by the device and selects the newest version that it recognizes. Error code 0x820e8 is sent if no (recognized) versions are offered. | ||
+ | |||
+ | Secondly, the device sends its "pairing identifier," if it recognizes the name/identifier of the host (from command 0x1). If the device does not recognize the host, it sends all-zeroes instead. If the host recognizes the pairing identifier for the device, it sends that same identifier back and expects command 0x4 next (and command 0x3, if sent, will result in error code 0x810e8). If the host doesn't recognize the identifier, it will either send error code 0x850e8 (if it isn't expecting to pair) or send a randomly-generated identifier and expect command 0x3 next. | ||
+ | |||
+ | == Command 0x3: Get secret key == | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |+ Command 0x3 input payload | ||
+ | |- | ||
+ | ! Offset | ||
+ | ! Size | ||
+ | ! Description | ||
+ | |- | ||
+ | | 0x0 || 0x20 || Unknown; always all zeroes? | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |+ Command 0x3 output payload | ||
+ | |- | ||
+ | ! Offset | ||
+ | ! Size | ||
+ | ! Description | ||
+ | |- | ||
+ | | 0x0 || 0x40 || Secret key | ||
+ | |} | ||
+ | |||
+ | When a new pairing is being established (see command 0x2), this is used to request the secret key generated for the pairing. It's currently unknown what purpose this "secret key" has. | ||
+ | |||
+ | When not expected (including when the RCD host isn't set up for pairing), it generates error code 0x810e8. | ||
+ | |||
+ | == Command 0x4: Finalize == | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |+ Command 0x4 input payload | ||
+ | |- | ||
+ | ! Offset | ||
+ | ! Size | ||
+ | ! Description | ||
+ | |- | ||
+ | | 0x0 || 0x20 || SHA256 of all payloads (not headers) up to this point after truncation to a multiple of 0x40 bytes. | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |+ Command 0x4 output payload | ||
+ | |- | ||
+ | ! Offset | ||
+ | ! Size | ||
+ | ! Description | ||
+ | |- | ||
+ | | 0x0 || 0x20 || SHA256 of all payloads (not headers) up to this point, including the input to this command. | ||
+ | |} | ||
+ | |||
+ | This is used to finalize the handshake. SHA256 hashes of the handshake up to this point are exchanged, as an integrity check. If the client sends the wrong hash, error code 0x830e8 is sent. If the device sends the correct hash, the host responds with its own hash, and carries on with using the device. | ||
+ | |||
+ | If the device accepts the response, it leaves the channel open, and activates other network services for the host to use. If the device rejects the response, it closes the channel, resets its networking stack, and attempts the connection again from scratch. Closing the handshake channel at any point afterward will trigger this same behavior. |
Latest revision as of 16:23, 26 February 2021
RCD is a framework that manages remote controlled devices - RC toys that connect to the Nintendo Switch via 802.11 ("WiFi") link. The first (and currently only) released product to use this framework is Mario Kart Live: Home Circuit.
RCD devices must be paired to the host Switch system in some way (e.g. QR code) and contain the connection settings in non-volatile memory so that they can reconnect on next use without needing to repeat the pairing process.
The RCD framework implements a very simple TCP-based RPC protocol that allows an RCD client to invoke methods on an RCD server. Note that multiple such connections may be established between the Switch and device.
RCD RPC
A client connects to a TCP server and sends as many commands as it likes, one at a time (and reading the response after each), before tearing down the TCP connection. It is not valid for a client to send a response to a server or for a server to send a request to a client.
Commands have a 0x10-byte header, followed by a payload of any size up to some maximum (typically 0x1000). All numbers are represented in big-endian format.
Offset | Size | Description |
---|---|---|
0x0 | 0x2 | Service being accessed |
0x2 | 0x2 | Command being invoked on that service |
0x4 | 0x4 | Payload length (PLEN) |
0x8 | 0x4 | Status code; ignored (and set to 0) for requests, an error code for responses |
0xC | 0x1 | Flags; currently only the 1s bit is significant: it indicates a response |
0xD | 0x3 | Padding; zero. |
0x10 | PLEN | The parameters to the command, or the response |
In theory, multiple "services" could be multiplexed over a single TCP connection, but in practice only a single service is allowed per TCP port.
Handshake protocol
Service ID: 0x0001
Commands: 4
When a device first establishes a wireless connection, it connects to the Switch on a well-known port to access the "handshake" service and make its presence known. A successful handshake causes application-specific RCD connections to be established. Commands must be sent in order (and no more than once), or error code 0x810e8 is generated by the host.
Command 0x1: Begin handshake
Offset | Size | Description |
---|---|---|
0x0 | 0x10 | Handshake protocol version; one byte followed by zero-padding. Must be 1. |
0x10 | 0x10 | Device "name" (e.g. 'Fuji'), followed by zero-padding. The Switch uses the empty string. |
0x20 | 0x10 | Device identifier. Devices embed their MAC as the last 6 bytes; host seems to use a randomly-generated UUID. |
0x30 | 0x20 | Nonce; cryptographic random bytes with no special purpose. |
This command is sent first (by the device). The host responds in kind. Error code 0x800e8 is generated if the handshake version is not 1.
Command 0x2: Version negotiation
Offset | Size | Description |
---|---|---|
0x0 | 0x20 | Pairing identifier |
0x20 | 0x1 | Number of versions offered |
0x21 | Varies | One 8-bit integer per version offered. There is no padding. |
Offset | Size | Description |
---|---|---|
0x0 | 0x20 | Pairing identifier |
0x20 | 0x1 | Selected version |
0x21 | 0xF | Zero-padding |
There are a few moving parts here.
Firstly, this command is responsible for negotiating a version for the underlying protocols. The host looks at all versions offered by the device and selects the newest version that it recognizes. Error code 0x820e8 is sent if no (recognized) versions are offered.
Secondly, the device sends its "pairing identifier," if it recognizes the name/identifier of the host (from command 0x1). If the device does not recognize the host, it sends all-zeroes instead. If the host recognizes the pairing identifier for the device, it sends that same identifier back and expects command 0x4 next (and command 0x3, if sent, will result in error code 0x810e8). If the host doesn't recognize the identifier, it will either send error code 0x850e8 (if it isn't expecting to pair) or send a randomly-generated identifier and expect command 0x3 next.
Command 0x3: Get secret key
Offset | Size | Description |
---|---|---|
0x0 | 0x20 | Unknown; always all zeroes? |
Offset | Size | Description |
---|---|---|
0x0 | 0x40 | Secret key |
When a new pairing is being established (see command 0x2), this is used to request the secret key generated for the pairing. It's currently unknown what purpose this "secret key" has.
When not expected (including when the RCD host isn't set up for pairing), it generates error code 0x810e8.
Command 0x4: Finalize
Offset | Size | Description |
---|---|---|
0x0 | 0x20 | SHA256 of all payloads (not headers) up to this point after truncation to a multiple of 0x40 bytes. |
Offset | Size | Description |
---|---|---|
0x0 | 0x20 | SHA256 of all payloads (not headers) up to this point, including the input to this command. |
This is used to finalize the handshake. SHA256 hashes of the handshake up to this point are exchanged, as an integrity check. If the client sends the wrong hash, error code 0x830e8 is sent. If the device sends the correct hash, the host responds with its own hash, and carries on with using the device.
If the device accepts the response, it leaves the channel open, and activates other network services for the host to use. If the device rejects the response, it closes the channel, resets its networking stack, and attempts the connection again from scratch. Closing the handshake channel at any point afterward will trigger this same behavior.