xsm3.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /*
  2. xsm3.c - part of libxsm3
  3. Copyright (C) 2022 InvoxiPlayGames
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with this library; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  15. */
  16. #include "xsm3.h"
  17. #include <stdbool.h>
  18. #include <stddef.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include "furi_hal_random.h"
  23. #include "excrypt.h"
  24. #include "usbdsec.h"
  25. // disable debugging by specifying XSM3_NO_DEBUGGING at compile time
  26. #ifndef XSM3_NO_DEBUGGING
  27. #define XSM3_printf printf
  28. #else
  29. #define XSM3_printf
  30. #endif // XSM3_NO_DEBUGGING
  31. // constant variables
  32. uint8_t xsm3_id_data_ms_controller[0x1D] = {
  33. 0x49, 0x4B, 0x00, 0x00, 0x17, 0x41, 0x41, 0x41,
  34. 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
  35. 0x00, 0x00, 0x80, 0x02, 0x09, 0x12, 0x82, 0x28,
  36. 0x03, 0x00, 0x01, 0x01, 0x71};
  37. // static global keys from the keyvault (shared across every retail system)
  38. static const uint8_t xsm3_key_0x1D[0x10] = {
  39. 0xE3, 0x5B, 0xFB, 0x1C, 0xCD, 0xAD, 0x32, 0x5B,
  40. 0xF7, 0x0E, 0x07, 0xFD, 0x62, 0x3D, 0xA7, 0xC4};
  41. static const uint8_t xsm3_key_0x1E[0x10] = {
  42. 0x8F, 0x29, 0x08, 0x38, 0x0B, 0x5B, 0xFE, 0x68,
  43. 0x7C, 0x26, 0x46, 0x2A, 0x51, 0xF2, 0xBC, 0x19};
  44. // retail keys for generating 0x23/0x24 keys from console ID
  45. static const uint8_t xsm3_root_key_0x23[0x10] = {
  46. 0x82, 0x80, 0x78, 0x68, 0x3A, 0x52, 0x3A, 0x98,
  47. 0x10, 0xF4, 0x0C, 0x12, 0x70, 0x66, 0xDC, 0xBA};
  48. static const uint8_t xsm3_root_key_0x24[0x10] = {
  49. 0x66, 0x62, 0x1A, 0x78, 0xF8, 0x60, 0x9C, 0x8A,
  50. 0x26, 0x9A, 0x04, 0xAE, 0xD8, 0x5C, 0x1E, 0xC8};
  51. // response to give to the given challenge command
  52. uint8_t xsm3_challenge_response[0x30];
  53. // console id fetched from challenge init packet
  54. uint8_t xsm3_console_id[0x8];
  55. // buffer to store the first key fetched from the KV (0x23)
  56. static uint8_t xsm3_kv_2des_key_1[0x10];
  57. // buffer to store the second key fetched from the KV (0x24)
  58. static uint8_t xsm3_kv_2des_key_2[0x10];
  59. // temporary buffer for decrypting packets
  60. static uint8_t xsm3_decryption_buffer[0x30];
  61. // identification data for the current device
  62. static uint8_t xsm3_identification_data[0x20];
  63. // random data sent from the console during the challenge init stage
  64. static uint8_t xsm3_random_console_data[0x10];
  65. static uint8_t xsm3_random_console_data_enc[0x10];
  66. static uint8_t xsm3_random_console_data_swap[0x10];
  67. static uint8_t xsm3_random_console_data_swap_enc[0x10];
  68. // random data set by the controller during the challenge init stage
  69. static uint8_t xsm3_random_controller_data[0x10];
  70. // hash of the decrypted data sent by the controller during challenge init
  71. static uint8_t xsm3_challenge_init_hash[0x14];
  72. void xsm3_initialise_state() {
  73. // set all variables to all zeroes
  74. memset(xsm3_challenge_response, 0, sizeof(xsm3_challenge_response));
  75. memset(xsm3_console_id, 0, sizeof(xsm3_console_id));
  76. memset(xsm3_kv_2des_key_1, 0, sizeof(xsm3_kv_2des_key_1));
  77. memset(xsm3_kv_2des_key_2, 0, sizeof(xsm3_kv_2des_key_2));
  78. memset(xsm3_decryption_buffer, 0, sizeof(xsm3_decryption_buffer));
  79. memset(xsm3_identification_data, 0, sizeof(xsm3_identification_data));
  80. memset(xsm3_random_console_data, 0, sizeof(xsm3_random_console_data));
  81. memset(xsm3_random_console_data_enc, 0, sizeof(xsm3_random_console_data_enc));
  82. memset(xsm3_random_console_data_swap, 0, sizeof(xsm3_random_console_data_swap));
  83. memset(xsm3_random_console_data_swap_enc, 0, sizeof(xsm3_random_console_data_swap_enc));
  84. memset(xsm3_random_controller_data, 0, sizeof(xsm3_random_controller_data));
  85. memset(xsm3_challenge_init_hash, 0, sizeof(xsm3_challenge_init_hash));
  86. }
  87. static uint8_t xsm3_calculate_checksum(const uint8_t* packet) {
  88. // packet length in header doesn't include the header itself
  89. uint8_t packet_length = packet[0x4] + 0x5;
  90. uint8_t checksum = 0x00;
  91. int i = 0;
  92. // checksum is just a XOR over all packet bytes
  93. for (i = 0x5; i < packet_length; i++) {
  94. checksum ^= packet[i];
  95. }
  96. // last byte of the packet is the checksum
  97. return checksum;
  98. }
  99. void xsm3_set_vid_pid(const uint8_t serial[0x0C], uint16_t vid, uint16_t pid) {
  100. memcpy(xsm3_id_data_ms_controller + 6, serial, 0x0C);
  101. uint8_t* id_data = xsm3_id_data_ms_controller;
  102. // skip over the packet header
  103. id_data += 0x5;
  104. // vendor ID
  105. memcpy(id_data + 0xf, &vid, sizeof(unsigned short));
  106. // product ID
  107. memcpy(id_data + 0x11, &pid, sizeof(unsigned short));
  108. xsm3_id_data_ms_controller[0x1C] = xsm3_calculate_checksum(xsm3_id_data_ms_controller);
  109. }
  110. static bool xsm3_verify_checksum(const uint8_t* packet) {
  111. // packet length in header doesn't include the header itself
  112. uint8_t packet_length = packet[0x4] + 0x5;
  113. // last byte of the packet is the checksum
  114. return (xsm3_calculate_checksum(packet) == packet[packet_length]);
  115. }
  116. void xsm3_set_identification_data(const uint8_t id_data[0x1D]) {
  117. // validate the checksum
  118. if (!xsm3_verify_checksum(id_data)) {
  119. XSM3_printf("[ Checksum failed when setting identification data! ]\n");
  120. }
  121. // skip over the packet header
  122. id_data += 0x5;
  123. // prepare the xsm3_identification_data buffer
  124. // contains serial number (len: 0xC), unknown (len: 0x2) and the "category node" to use (len: 0x1)
  125. memcpy(xsm3_identification_data, id_data, 0xF);
  126. // vendor ID
  127. memcpy(xsm3_identification_data + 0x10, id_data + 0xF, sizeof(unsigned short));
  128. // product ID
  129. memcpy(xsm3_identification_data + 0x12, id_data + 0x11, sizeof(unsigned short));
  130. // unknown
  131. memcpy(xsm3_identification_data + 0x14, id_data + 0x13, sizeof(unsigned char));
  132. // unknown
  133. memcpy(xsm3_identification_data + 0x15, id_data + 0x16, sizeof(unsigned char));
  134. // unknown
  135. memcpy(xsm3_identification_data + 0x16, id_data + 0x14, sizeof(unsigned short));
  136. }
  137. void xsm3_generate_kv_keys(const uint8_t console_id[0x8]) {
  138. // make a sha-1 hash of the console id
  139. uint8_t console_id_hash[0x14];
  140. ExCryptSha(console_id, 0x8, NULL, 0, NULL, 0, console_id_hash, 0x14);
  141. // encrypt it with the root keys for 1st party controllers
  142. UsbdSecXSM3AuthenticationCrypt(xsm3_root_key_0x23, console_id_hash, 0x10, xsm3_kv_2des_key_1, 1);
  143. UsbdSecXSM3AuthenticationCrypt(xsm3_root_key_0x24, console_id_hash + 0x4, 0x10, xsm3_kv_2des_key_2, 1);
  144. }
  145. void xsm3_do_challenge_init(uint8_t challenge_packet[0x22]) {
  146. uint8_t incoming_packet_mac[0x8];
  147. uint8_t response_packet_mac[0x8];
  148. int i = 0;
  149. // validate the checksum
  150. if (!xsm3_verify_checksum(challenge_packet)) {
  151. XSM3_printf("[ Checksum failed when validating challenge init! ]\n");
  152. }
  153. // decrypt the packet content using the static key from the keyvault
  154. UsbdSecXSM3AuthenticationCrypt(xsm3_key_0x1D, challenge_packet + 0x5, 0x18, xsm3_decryption_buffer, 0);
  155. // first 0x10 bytes are random data
  156. memcpy(xsm3_random_console_data, xsm3_decryption_buffer, 0x10);
  157. // next 0x8 bytes are from the console certificate
  158. memcpy(xsm3_console_id, xsm3_decryption_buffer + 0x10, 0x8);
  159. // last 4 bytes of the packet are the last 4 bytes of the MAC
  160. UsbdSecXSM3AuthenticationMac(xsm3_key_0x1E, NULL, challenge_packet + 5, 0x18, incoming_packet_mac);
  161. // validate the MAC
  162. if (memcmp(incoming_packet_mac + 4, challenge_packet + 0x5 + 0x18, 0x4) != 0) {
  163. XSM3_printf("[ MAC failed when validating challenge init! ]\n");
  164. }
  165. xsm3_generate_kv_keys(xsm3_console_id);
  166. // the random value is swapped at an 8 byte boundary
  167. memcpy(xsm3_random_console_data_swap, xsm3_random_console_data + 0x8, 0x8);
  168. memcpy(xsm3_random_console_data_swap + 0x8, xsm3_random_console_data, 0x8);
  169. // and then encrypted - the regular value encrypted with key 1, the swapped value encrypted with key 2
  170. UsbdSecXSM3AuthenticationCrypt(xsm3_kv_2des_key_1, xsm3_random_console_data, 0x10, xsm3_random_console_data_enc, 1);
  171. UsbdSecXSM3AuthenticationCrypt(xsm3_kv_2des_key_2, xsm3_random_console_data_swap, 0x10, xsm3_random_console_data_swap_enc, 1);
  172. // generate random data
  173. for (i = 0; i < 0x10; i++) {
  174. xsm3_random_controller_data[i] = furi_hal_random_get() & 0xFF;
  175. }
  176. // clear response buffers
  177. memset(xsm3_challenge_response, 0, sizeof(xsm3_challenge_response));
  178. memset(xsm3_decryption_buffer, 0, sizeof(xsm3_decryption_buffer));
  179. // set header and packet length of challenge response
  180. xsm3_challenge_response[0] = 0x49; // packet magic
  181. xsm3_challenge_response[1] = 0x4C;
  182. xsm3_challenge_response[4] = 0x28; // packet length
  183. // copy random controller, random console data to the encryption buffer
  184. memcpy(xsm3_decryption_buffer, xsm3_random_controller_data, 0x10);
  185. memcpy(xsm3_decryption_buffer + 0x10, xsm3_random_console_data, 0x10);
  186. // save the sha1 hash of the decrypted contents for later
  187. ExCryptSha(xsm3_decryption_buffer, 0x20, NULL, 0, NULL, 0, xsm3_challenge_init_hash, 0x14);
  188. // encrypt challenge response packet using the encrypted random key
  189. UsbdSecXSM3AuthenticationCrypt(xsm3_random_console_data_enc, xsm3_decryption_buffer, 0x20, xsm3_challenge_response + 0x5, 1);
  190. // calculate MAC using the encrypted swapped random key and use it to calculate ACR
  191. UsbdSecXSM3AuthenticationMac(xsm3_random_console_data_swap_enc, NULL, xsm3_challenge_response + 0x5, 0x20, response_packet_mac);
  192. // calculate ACR and append to the end of the xsm3_challenge_response
  193. UsbdSecXSMAuthenticationAcr(xsm3_console_id, xsm3_identification_data, response_packet_mac, xsm3_challenge_response + 0x5 + 0x20);
  194. // calculate the checksum for the response packet
  195. xsm3_challenge_response[0x5 + 0x28] = xsm3_calculate_checksum(xsm3_challenge_response);
  196. // the console random value changes slightly after this point
  197. memcpy(xsm3_random_console_data, xsm3_random_controller_data + 0xC, 0x4);
  198. memcpy(xsm3_random_console_data + 0x4, xsm3_random_console_data + 0xC, 0x4);
  199. }
  200. void xsm3_do_challenge_verify(uint8_t challenge_packet[0x16]) {
  201. uint8_t incoming_packet_mac[0x8];
  202. // validate the checksum
  203. if (!xsm3_verify_checksum(challenge_packet)) {
  204. XSM3_printf("[ Checksum failed when validating challenge verify! ]\n");
  205. }
  206. // decrypt the packet using the controller generated random value
  207. UsbdSecXSM3AuthenticationCrypt(xsm3_random_controller_data, challenge_packet + 0x5, 0x8, xsm3_decryption_buffer, 0);
  208. // replace part of our random encryption value with the decrypted buffer
  209. memcpy(xsm3_random_console_data + 0x8, xsm3_decryption_buffer, 0x8);
  210. // calculate the MAC of the incoming packet
  211. UsbdSecXSM3AuthenticationMac(xsm3_challenge_init_hash, xsm3_random_console_data, challenge_packet + 0x5, 0x8, incoming_packet_mac);
  212. // validate the MAC
  213. if (memcmp(incoming_packet_mac, challenge_packet + 0x5 + 0x8, 0x8) != 0) {
  214. XSM3_printf("[ MAC failed when validating challenge verify! ]\n");
  215. }
  216. // clear response buffers
  217. memset(xsm3_challenge_response, 0, sizeof(xsm3_challenge_response));
  218. memset(xsm3_decryption_buffer, 0, sizeof(xsm3_decryption_buffer));
  219. // set header and packet length of challenge response
  220. xsm3_challenge_response[0] = 0x49; // packet magic
  221. xsm3_challenge_response[1] = 0x4C;
  222. xsm3_challenge_response[4] = 0x10; // packet length
  223. // calculate the ACR value and encrypt it into the outgoing packet using the encrypted random
  224. UsbdSecXSMAuthenticationAcr(xsm3_console_id, xsm3_identification_data, xsm3_random_console_data + 0x8, xsm3_decryption_buffer);
  225. UsbdSecXSM3AuthenticationCrypt(xsm3_random_console_data_enc, xsm3_decryption_buffer, 0x8, xsm3_challenge_response + 0x5, 1);
  226. // calculate the MAC of the encrypted packet and append it to the end
  227. UsbdSecXSM3AuthenticationMac(xsm3_random_console_data_swap_enc, xsm3_random_console_data, xsm3_challenge_response + 0x5, 0x8, xsm3_challenge_response + 0x5 + 0x8);
  228. // calculate the checksum for the response packet
  229. xsm3_challenge_response[0x5 + 0x10] = xsm3_calculate_checksum(xsm3_challenge_response);
  230. }