# Ring Keychip The Ring keychip is arguably simultaniously one of the most overkill while least utilised parts of the system. On-board is a PIC microcontroller, a dedicated cryptography chip, a hardware SHA engine for authentication, and flash storage. ## Protocol The PIC communicates with the system using a parallel bus. This bus is exposed physically on the keychip connector, and in software can be accessed using `\\.\mxparallel`. All bus communication is encrypted using AES 128 ECB, using a different key for each data direction. Send/receive is defined from the perspective of the Ring system. That is, the "Send" key handles data from the Ring to the keychip, and the "Receive" key handles data from the keychip to the Ring. The initial key values are: ### Initial receive key: ``` 75 6f 72 61 74 6e 65 6b 61 6d 69 68 73 75 6b 75 ``` ### Initial send key: ``` 66 6E 65 6B 65 72 61 77 64 72 61 68 61 67 65 73 ``` All packets are first prefixed by a command ordinal (see below), then command-specific information. The base unit of transfer is 16 bytes due to AES 128. Unused bytes can contain anything, however mxkeychip chooses to pad using random bytes derrived from the current system time. ## Storage locations ### EEPROM Stores entries detailing the tracedata metadata. There is a copy of the structure at 0x000 and a duplicate at 0x100. Each structure is comprised of 16 entries, where each entry first has a 4 byte CRC, then 12 bytes currently unknown. ### NVRAM | Start | End | Content | | ---------------- | ---------------- | ------------------ | | 1800h | 1bffh | billing public key | | 1c00h | 1fffh | ca certificate | Each block begins with a 4 byte integer indicating the length of the data following it (up to 1020 bytes). ### Flash | Start | End | Content | | ----------------- | ----------------- | ------------------ | | 00000h | 0ffffh | tracedata sector 1 | | 10000h | 1ffffh | tracedata sector 2 | | 20000h | 2ffffh | tracedata sector 3 | | 30000h | 3ffffh | tracedata sector 4 | | 40000h | 4ffffh | tracedata sector 5 | | 50000h | 5ffffh | tracedata sector 6 | | 60000h | 6ffffh | tracedata sector 7 | | 70000h | 70fffh | nvram0 | | 71000h | 71fffh | nvram1 | | 72000h | 72fffh | nvram2 | | 73000h | 73fffh | nvram3 | | 74000h | 74fffh | nvram4 | | 75000h | 75fffh | nvram5 | | 76000h | 76fffh | nvram6 | | 77000h | 77fffh | nvram7 | | 78000h | 78fffh | nvram8 | | 79000h | 79fffh | nvram9 | | 7a000h | 7a10bh | billing info | | 7b000h | 7b10bh | billing info dup | Each tracedata sector begins with a 128-byte bitfield (1024 bits) indicating which blocks in the sector have been populated. Following this are 1022 blocks, 64 bytes each. (the last two of the theorhetical 1024 would overlap into the next sector). Each slot is individually encrypted with DES ECB, using key `4D77F1748D6D1094`. This is implemented via the `AT25DF041A` 4Mbit flash chip on the keychip PCB. The maximum address is therefore 7ffffh. ## Command Ordinals | Ordinal | Command | | ------- | --------------------------------- | | `0` | [SetKeyS](#setkeys) | | `1` | [SetKeyR](#setkeyr) | | `2` | [SetIv](#setiv) | | `3` | [Decrypt](#decrypt) | | `4` | [Encrypt](#encrypt) | | `5` | [GetAppBootInfo](#getappbootinfo) | | `6` | [EepromWrite](#eepromwrite) | | `7` | [EepromRead](#eepromread) | | `8` | [NvramWrite](#nvramwrite) | | `9` | [NvramRead](#nvramread) | | `10` | [AddPlayCount](#addplaycount) | | `11` | [FlashRead](#flashread) | | `12` | [FlashErase](#flasherase) | | `13` | ? | | `14` | [FlashWrite](#flashwrite) | | `15` | ? | | `16` | ? | | `17` | ? | | `18` | ? | | `19` | ? | | `20` | [KcGetVersion](#kcgetversion) | | `21` | [SetMainId](#setmainid) | | `22` | [GetMainId](#getmainid) | | `23` | [SetKeyId](#setkeyid) | | `24` | [GetKeyId](#getkeyid) | | `25` | [GetPlayCounter](#getplaycounter) | ## SetKeyS Sets the "send" encryption key. The key is changed before communication of the reply. | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 0 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | ^ | key | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | | | Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | Variable | | | -------- | ------------ | | key | The send key | ## SetKeyR Sets the "receive" encryption key. The key is changed before communication of the reply. | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 1 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | ^ | key | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | | | Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | Variable | | | -------- | --------------- | | key | The receive key | ## SetIv Reset the game key IV to its initial value | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 2 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | ## Decrypt Decrypt a block of data using the game key | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 3 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | | ct | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | | | Receive | pt | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | Variable | | | -------- | ------------------------ | | ct | The encrypted ciptertext | | pt | The decrypted plaintext | ## Encrypt Encrypt a block of data using the game key | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 4 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | ^ | pt | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | | | Receive | ct | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | Variable | | | -------- | ------------------------ | | pt | The decrypted plaintext | | ct | The encrypted ciptertext | ## GetAppBootInfo Request the AppBoot structure from the keychip. The AppBoot structure is 256 bytes, thus requires 16 packets. | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ------------------- | --- | --- | -------- | --------------- | --- | --- | --- | ------- | --- | --- | --- | ------ | ---------- | ----------- | --- | | Send | 5 | 0 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | CRC | < | < | < | Format | < | < | < | Game ID | < | < | < | Region | Model type | System flag | ? | | | Platform ID | < | < | DVD flag | Network address | < | < | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | _206 bytes padding_ | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | | | | | | | | | | | | | | | | | | | ^ | Seed | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | Variable | | | --------------- | ----------------------------------------------------- | | CRC | CRC32 checksum of the reamining 252 bytes | | Format | AppBoot format type. The described type is format `1` | | Game ID | Four character game ID. For example, `SBTR` | | Region | Region bitmask | | Model type | Model type this keychip is for | | System flag | System flag bitmask | | DVD flag | Is update from DVD allowed | | Network address | Permitted subnet. Fourth octet is null | **Note:** I'm not sure what the `0` is for here. Surprises await for anything other than `0` :D ## EepromWrite | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ---- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 6 | reg | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | ^ | data | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | | | Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | Variable | | | -------- | --------------------------------------------------------------------- | | reg | EEPROM register to write. EEPROM is divided into 16x16 byte registers | | data | The data to write | ## EepromRead | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ---- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 7 | reg | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | data | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | Variable | | | -------- | -------------------------------------------------------------------- | | reg | EEPROM register to read. EEPROM is divided into 16x16 byte registers | | data | The data read | ## NvramWrite | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ---- | ---- | --- | ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 8 | addr | < | blocks | * | * | * | * | * | * | * | * | * | * | * | * | | ^ | data | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | | | Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | Variable | | | -------- | ----------------------------------------------------- | | addr | Absolute offset in NVRAM to write at | | blocks | The number of 16 bytes blocks to write | | data | The data to write. This will require `blocks` packets | ## NvramRead | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ---- | ---- | --- | ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 8 | addr | < | blocks | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | data | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | Variable | | | -------- | ------------------------------------------------- | | addr | Absolute offset in NVRAM to read at | | blocks | The number of 16 bytes blocks to read | | data | The read data. This will require `blocks` packets | ## AddPlayCount Increment the playcount value | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 10 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | ## FlashRead | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ---- | --- | ---- | --- | --- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 11 | addr | < | < | # bytes | < | < | * | * | * | * | * | * | * | * | * | | Variable | | | -------- | ----------------------------------- | | addr | Absolute offset in flash to read at | | # bytes | The number of bytes to read | **Important:** Data is returned as raw bytes transmitted over parallel, without encryption or packet encapsulation. ## FlashErase Honestly not actually sure if this is an erase or what. Still working on it! | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | --- | ---- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 12 | addr | < | < | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | ## FlashWrite | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | --- | ---- | --- | --- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 14 | addr | < | < | # bytes | < | < | * | * | * | * | * | * | * | * | * | | | | Receive | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | Variable | | | -------- | ------------------------------------ | | addr | Absolute offset in flash to write at | | # bytes | The number of bytes to write | **Important:** Data is transmitted immediately following transmission of the first packet. Is it transmitted as raw bytes over parallel, without encryption or packet encapsulation. The response packet is transmitted after # bytes have been received in this manner. ## KcGetVersion | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 20 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | version | < | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | Variable | | | -------- | --------------------------------------------------------------- | | version | Two-byte version number. Keychips are observed to return 01,04. | ## SetMainId Set the Main ID on the keychip, if not already set. | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 21 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | ^ | Main ID | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | | | Receive | err | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | Variable | | | -------- | ------------------------------------------------------------------ | | Main ID | The new keychip Main ID. No validation is performed of this value. | | err | ? (0?) when succesful, FFh otherwise | ## GetMainId Get the Main ID on the keychip. | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 22 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | Main ID | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | Variable | | | -------- | ------------------- | | Main ID | The keychip Main ID | ## SetKeyId Set the Key ID on the keychip, if not already set. | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 23 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | ^ | Key ID | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | | | Receive | err | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | Variable | | | -------- | ------------------------------------------------------------- | | Key ID | The new keychip ID. No validation is performed of this value. | | err | ? (0?) when succesful, FFh otherwise | ## GetKeyId Get the Key ID on the keychip. | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 24 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | Key ID | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | | Variable | | | -------- | -------------- | | Key ID | The keychip ID | ## GetPlayCounter Get the current playcount value | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | | ------- | --------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Send | 24 | * | * | * | * | * | * | * | * | * | * | * | * | * | * | * | | | | Receive | playcount | < | < | < | * | * | * | * | * | * | * | * | * | * | * | * | | Variable | | | --------- | --------------------------- | | playcount | The billing playcount value |