Explorar el Código

Merge portal_of_flipper from https://github.com/sanjay900/portal_of_flipper

WillyJL hace 8 meses
padre
commit
4ce09bde4a

+ 2 - 0
portal_of_flipper/CHANGELOG.md

@@ -1,3 +1,5 @@
+## 1.3
+ - Implement auth for the xbox 360
 ## 1.2
  - Several bug fixes and changes to make the portal emulation closer to the real portals
  - Use the flippers speaker to emulate the portals speaker

+ 33 - 2
portal_of_flipper/helpers/pof_usb_xbox360.c

@@ -1,4 +1,6 @@
 #include "pof_usb.h"
+#include "xsm3/xsm3.h"
+#include "furi_hal_random.h"
 
 #define TAG "POF USB XBOX360"
 
@@ -56,7 +58,6 @@ static int32_t pof_thread_worker(void* context) {
         uint32_t now = furi_get_tick();
         uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, timeout);
         if (flags & EventRx) {  // fast flag
-
             uint8_t buf[POF_USB_RX_MAX_SIZE];
             len_data = pof_usb_receive(dev, buf, POF_USB_RX_MAX_SIZE);
             // 360 controller packets have a header of 0x0b 0x14
@@ -514,13 +515,14 @@ static const struct PoFUsbDescriptorXbox360 usb_pof_cfg_descr_x360 = {
         {0x06, 0x41, 0x00, 0x01, 0x01, 0x03},
 };
 
+uint8_t serial[0x0C];
+short state = 2;  // 1 = in-progress, 2 = complete
 /* Control requests handler */
 static usbd_respond
 pof_hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
     UNUSED(callback);
     uint8_t wValueH = req->wValue >> 8;
     uint8_t wValueL = req->wValue & 0xFF;
-
     if (req->bmRequestType == 0xC0 && req->bRequest == USB_HID_GETREPORT && req->wValue == 0x0000) {
         dev->status.data_ptr = (uint8_t*)xbox_serial;
         dev->status.data_count = sizeof(xbox_serial);
@@ -539,6 +541,35 @@ pof_hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback)
     if (req->bmRequestType == 0x41 && req->bRequest == 00 && (req->wValue == 0x1F || req->wValue == 0x1E)) {
         return usbd_ack;
     }
+    switch (req->bRequest) {
+        case 0x81:
+            uint8_t serial[0x0C];
+            for (size_t i = 0; i < sizeof(serial); i++) {
+                serial[i] = furi_hal_random_get() & 0xFF;
+            }
+            xsm3_set_vid_pid(serial, POF_USB_VID, POF_USB_PID);
+            xsm3_initialise_state();
+            xsm3_set_identification_data(xsm3_id_data_ms_controller);
+            dev->status.data_ptr = (uint8_t*)(xsm3_id_data_ms_controller);
+            dev->status.data_count = sizeof(xsm3_id_data_ms_controller);
+            return usbd_ack;
+        case 0x82:
+            xsm3_do_challenge_init((uint8_t *)dev->status.data_ptr);
+            return usbd_ack;
+        case 0x87:
+            xsm3_do_challenge_verify((uint8_t *)dev->status.data_ptr);
+            return usbd_ack;
+        case 0x84:
+            return usbd_ack;
+        case 0x83:
+            dev->status.data_ptr = (uint8_t*)(xsm3_challenge_response);
+            dev->status.data_count = req->wLength;
+            return usbd_ack;
+        case 0x86:
+            dev->status.data_ptr = (uint8_t*)&(state);
+            dev->status.data_count = sizeof(state);
+            return usbd_ack;
+    }
 
     if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
             (USB_REQ_DEVICE | USB_REQ_STANDARD) &&

+ 39 - 0
portal_of_flipper/xsm3/excrypt.h

@@ -0,0 +1,39 @@
+#ifndef EXCRYPT_H_
+#define EXCRYPT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <stdint.h>
+
+#if(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#define SWAP16(i) i
+#define SWAP32(i) i
+#define SWAP64(i) i
+#else
+#define SWAP16(i) ((((i) & 0xFF) << 8 | ((i) >> 8) & 0xFF) & 0xFFFF)
+#define SWAP32(i) ((((i) & 0xff) << 24) | (((i) & 0xff00) << 8) | (((i) & 0xff0000) >> 8) | (((i) >> 24) & 0xff))
+#define SWAP64(i) ((SWAP32((i) & 0xFFFFFFFF) << 32) | (SWAP32(((i) >> 32) & 0xFFFFFFFF)))
+#endif
+
+#define U8V(data) ((uint8_t)(data) & 0xFF)
+#define ROTL8(data, bits) (U8V((data) << (bits)) | ((data) >> (8 - (bits))))
+
+#define U16V(data) ((uint16_t)(data) & 0xFFFF)
+#define ROTL16(data, bits) (U16V((data) << (bits)) | ((data) >> (16 - (bits))))
+
+#define U32V(data) ((uint32_t)(data) & 0xFFFFFFFF)
+#define ROTL32(data, bits) (U32V((data) << (bits)) | ((data) >> (32 - (bits))))
+
+#define ROTL64(data, bits) (((data) << (bits)) | ((data) >> (64 - (bits))))
+
+typedef int BOOL;
+
+#include "excrypt_des.h"
+#include "excrypt_sha.h"
+#include "excrypt_parve.h"
+#ifdef __cplusplus
+}
+#endif
+
+#endif // EXCRYPT_H_

+ 198 - 0
portal_of_flipper/xsm3/excrypt_des.c

@@ -0,0 +1,198 @@
+#include "excrypt.h"
+#include "excrypt_des_data.h"
+#include <string.h>
+
+#define LODWORD(_qw)    ((uint32_t)(_qw))
+#define HIDWORD(_qw)    ((uint32_t)(((_qw) >> 32) & 0xffffffff))
+
+// DES code based on https://github.com/fffaraz/cppDES
+
+void ExCryptDesParity(const uint8_t* input, uint32_t input_size, uint8_t* output)
+{
+  for (uint32_t i = 0; i < input_size; i++)
+  {
+    uint8_t parity = input[i];
+
+    parity ^= parity >> 4;
+    parity ^= parity >> 2;
+    parity ^= parity >> 1;
+
+    output[i] = (input[i] & 0xFE) | (~parity & 1);
+  }
+}
+
+void ExCryptDesKey(EXCRYPT_DES_STATE* state, const uint8_t* key)
+{
+  uint64_t qkey = SWAP64(*(const uint64_t*)key);
+
+  // initial key schedule calculation
+  uint64_t permuted_choice_1 = 0; // 56 bits
+  for (int i = 0; i < 56; i++)
+  {
+    permuted_choice_1 <<= 1;
+    permuted_choice_1 |= (qkey >> (64 - PC1[i])) & LB64_MASK;
+  }
+
+  // 28 bits
+  uint32_t C = (uint32_t)((permuted_choice_1 >> 28) & 0x000000000fffffff);
+  uint32_t D = (uint32_t)(permuted_choice_1 & 0x000000000fffffff);
+
+  // Calculation of the 16 keys
+  for (int i = 0; i < 16; i++)
+  {
+    // key schedule, shifting Ci and Di
+    for (int j = 0; j < ITERATION_SHIFT[i]; j++)
+    {
+      C = (0x0fffffff & (C << 1)) | (0x00000001 & (C >> 27));
+      D = (0x0fffffff & (D << 1)) | (0x00000001 & (D >> 27));
+    }
+
+    uint64_t permuted_choice_2 = (((uint64_t)C) << 28) | (uint64_t)D;
+
+    uint64_t sub_key = 0; // 48 bits (2*24)
+    for (int j = 0; j < 48; j++)
+    {
+      sub_key <<= 1;
+      sub_key |= (permuted_choice_2 >> (56 - PC2[j])) & LB64_MASK;
+    }
+    state->keytab[i] = sub_key;
+  }
+}
+
+uint32_t f(uint32_t R, uint64_t k)
+{
+  // applying expansion permutation and returning 48-bit data
+  uint64_t s_input = 0;
+  for (int i = 0; i < 48; i++)
+  {
+    s_input <<= 1;
+    s_input |= (uint64_t)((R >> (32 - EXPANSION[i])) & LB32_MASK);
+  }
+
+  // XORing expanded Ri with Ki, the round key
+  s_input = s_input ^ k;
+
+  // applying S-Boxes function and returning 32-bit data
+  uint32_t s_output = 0;
+  for (int i = 0; i < 8; i++)
+  {
+    // Outer bits
+    char row = (char)((s_input & (0x0000840000000000 >> 6 * i)) >> (42 - 6 * i));
+    row = (row >> 4) | (row & 0x01);
+
+    // Middle 4 bits of input
+    char column = (char)((s_input & (0x0000780000000000 >> 6 * i)) >> (43 - 6 * i));
+
+    s_output <<= 4;
+    s_output |= (uint32_t)(SBOX[i][16 * row + column] & 0x0f);
+  }
+
+  // applying the round permutation
+  uint32_t f_result = 0;
+  for (int i = 0; i < 32; i++)
+  {
+    f_result <<= 1;
+    f_result |= (s_output >> (32 - PBOX[i])) & LB32_MASK;
+  }
+
+  return f_result;
+}
+
+void feistel(uint32_t* L, uint32_t* R, uint32_t F)
+{
+  uint32_t temp = *R;
+  *R = *L ^ F;
+  *L = temp;
+}
+
+void ExCryptDesEcb(const EXCRYPT_DES_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt)
+{
+  uint64_t block;
+  memcpy(&block, input, sizeof(uint64_t));
+  block = SWAP64(block);
+  //uint64_t block = SWAP64(*(uint64_t*)input)
+
+
+  // initial permutation
+  uint64_t result = 0;
+  for (int i = 0; i < 64; i++)
+  {
+    result <<= 1;
+    result |= (block >> (64 - IP[i])) & LB64_MASK;
+  }
+
+  // dividing T' into two 32-bit parts
+  uint32_t L = HIDWORD(result);
+  uint32_t R = LODWORD(result);
+
+  // 16 rounds
+  for (int i = 0; i < 16; i++)
+  {
+    uint32_t F = !encrypt ? f(R, state->keytab[15 - i]) : f(R, state->keytab[i]);
+    feistel(&L, &R, F);
+  }
+
+  // swapping the two parts
+  block = (((uint64_t)R) << 32) | (uint64_t)L;
+
+  // inverse initial permutation
+  result = 0;
+  for (int i = 0; i < 64; i++)
+  {
+    result <<= 1;
+    result |= (block >> (64 - FP[i])) & LB64_MASK;
+  }
+  result = SWAP64(result);
+  memcpy(output, &result, sizeof(result));
+}
+
+void ExCryptDes3Key(EXCRYPT_DES3_STATE* state, const uint64_t* keys)
+{
+  ExCryptDesKey(&state->des_state[0], (const uint8_t*)& keys[0]);
+  ExCryptDesKey(&state->des_state[1], (const uint8_t*)& keys[1]);
+  ExCryptDesKey(&state->des_state[2], (const uint8_t*)& keys[2]);
+}
+
+void ExCryptDes3Ecb(const EXCRYPT_DES3_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt)
+{
+  if (encrypt)
+  {
+    ExCryptDesEcb(&state->des_state[0], input, output, encrypt);
+    ExCryptDesEcb(&state->des_state[1], output, output, !encrypt);
+    ExCryptDesEcb(&state->des_state[2], output, output, encrypt);
+  }
+  else
+  {
+    ExCryptDesEcb(&state->des_state[2], input, output, encrypt);
+    ExCryptDesEcb(&state->des_state[1], output, output, !encrypt);
+    ExCryptDesEcb(&state->des_state[0], output, output, encrypt);
+  }
+}
+
+void ExCryptDes3Cbc(const EXCRYPT_DES3_STATE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed, uint8_t encrypt)
+{
+  uint64_t last_block = *(uint64_t*)feed;
+  for (uint32_t i = 0; i < input_size / 8; i++)
+  {
+    if (encrypt) {
+      uint64_t temp;
+      memcpy(&temp, input, sizeof(temp));
+      temp = temp ^ last_block;
+      memcpy(output, &temp, sizeof(temp));
+      ExCryptDes3Ecb(state, output, output, encrypt);
+      memcpy(&last_block, output, sizeof(last_block));
+    }
+    else
+    {
+      ExCryptDes3Ecb(state, input, output, encrypt);
+      uint64_t temp;
+      memcpy(&temp, output, sizeof(temp));
+      temp = temp ^ last_block;
+      memcpy(output, &temp, sizeof(temp));
+      memcpy(&last_block, input, sizeof(last_block));
+    }
+    input += 8;
+    output += 8;
+  }
+  *(uint64_t*)feed = last_block;
+}

+ 24 - 0
portal_of_flipper/xsm3/excrypt_des.h

@@ -0,0 +1,24 @@
+#ifndef EXCRYPT_DES_H_
+#define EXCRYPT_DES_H_
+// DES & 3DES functions
+
+typedef struct _EXCRYPT_DES_STATE
+{
+  uint64_t keytab[16];
+} EXCRYPT_DES_STATE;
+
+void ExCryptDesParity(const uint8_t* input, uint32_t input_size, uint8_t* output);
+
+void ExCryptDesKey(EXCRYPT_DES_STATE* state, const uint8_t* key);
+void ExCryptDesEcb(const EXCRYPT_DES_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt);
+
+typedef struct _EXCRYPT_DES3_STATE
+{
+  EXCRYPT_DES_STATE des_state[3];
+} EXCRYPT_DES3_STATE;
+
+void ExCryptDes3Key(EXCRYPT_DES3_STATE* state, const uint64_t* keys);
+void ExCryptDes3Ecb(const EXCRYPT_DES3_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt);
+void ExCryptDes3Cbc(const EXCRYPT_DES3_STATE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed, uint8_t encrypt);
+
+#endif // EXCRYPT_DES_H_

+ 157 - 0
portal_of_flipper/xsm3/excrypt_des_data.h

@@ -0,0 +1,157 @@
+#ifndef EXCRYPT_DES_DATA_H_
+#define EXCRYPT_DES_DATA_H_
+// Data needed by DES/3DES functions
+// (only included by excrypt_des.c - no headers should include this!)
+
+#define LB32_MASK 0x00000001
+#define LB64_MASK 0x0000000000000001
+#define L64_MASK  0x00000000ffffffff
+
+// Initial Permutation Table [8*8]
+static const char IP[] =
+{
+    58, 50, 42, 34, 26, 18, 10, 2,
+    60, 52, 44, 36, 28, 20, 12, 4,
+    62, 54, 46, 38, 30, 22, 14, 6,
+    64, 56, 48, 40, 32, 24, 16, 8,
+    57, 49, 41, 33, 25, 17,  9, 1,
+    59, 51, 43, 35, 27, 19, 11, 3,
+    61, 53, 45, 37, 29, 21, 13, 5,
+    63, 55, 47, 39, 31, 23, 15, 7
+};
+
+// Inverse Initial Permutation Table [8*8]
+static const char FP[] =
+{
+    40, 8, 48, 16, 56, 24, 64, 32,
+    39, 7, 47, 15, 55, 23, 63, 31,
+    38, 6, 46, 14, 54, 22, 62, 30,
+    37, 5, 45, 13, 53, 21, 61, 29,
+    36, 4, 44, 12, 52, 20, 60, 28,
+    35, 3, 43, 11, 51, 19, 59, 27,
+    34, 2, 42, 10, 50, 18, 58, 26,
+    33, 1, 41,  9, 49, 17, 57, 25
+};
+
+// Expansion table [6*8]
+static const char EXPANSION[] =
+{
+    32,  1,  2,  3,  4,  5,
+     4,  5,  6,  7,  8,  9,
+     8,  9, 10, 11, 12, 13,
+    12, 13, 14, 15, 16, 17,
+    16, 17, 18, 19, 20, 21,
+    20, 21, 22, 23, 24, 25,
+    24, 25, 26, 27, 28, 29,
+    28, 29, 30, 31, 32,  1
+};
+
+// The S-Box tables [8*16*4]
+static const char SBOX[8][64] =
+{
+    {
+    // S1
+    14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,
+     0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,
+     4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
+    15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13
+},
+{
+  // S2
+  15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,
+   3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,
+   0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
+  13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9
+},
+{
+  // S3
+  10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,
+  13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,
+  13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
+   1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12
+},
+{
+  // S4
+   7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,
+  13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,
+  10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
+   3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14
+},
+{
+  // S5
+   2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,
+  14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,
+   4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
+  11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3
+},
+{
+  // S6
+  12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,
+  10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,
+   9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
+   4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13
+},
+{
+  // S7
+   4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,
+  13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,
+   1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
+   6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12
+},
+{
+  // S8
+  13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7,
+   1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,
+   7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,
+   2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11
+}
+};
+
+// Post S-Box permutation [4*8]
+static const char PBOX[] =
+{
+    16,  7, 20, 21,
+    29, 12, 28, 17,
+     1, 15, 23, 26,
+     5, 18, 31, 10,
+     2,  8, 24, 14,
+    32, 27,  3,  9,
+    19, 13, 30,  6,
+    22, 11,  4, 25
+};
+
+// Permuted Choice 1 Table [7*8]
+static const char PC1[] =
+{
+    57, 49, 41, 33, 25, 17,  9,
+     1, 58, 50, 42, 34, 26, 18,
+    10,  2, 59, 51, 43, 35, 27,
+    19, 11,  3, 60, 52, 44, 36,
+
+    63, 55, 47, 39, 31, 23, 15,
+     7, 62, 54, 46, 38, 30, 22,
+    14,  6, 61, 53, 45, 37, 29,
+    21, 13,  5, 28, 20, 12,  4
+};
+
+// Permuted Choice 2 Table [6*8]
+static const char PC2[] =
+{
+    14, 17, 11, 24,  1,  5,
+     3, 28, 15,  6, 21, 10,
+    23, 19, 12,  4, 26,  8,
+    16,  7, 27, 20, 13,  2,
+    41, 52, 31, 37, 47, 55,
+    30, 40, 51, 45, 33, 48,
+    44, 49, 39, 56, 34, 53,
+    46, 42, 50, 36, 29, 32
+};
+
+// Iteration Shift Array
+static const char ITERATION_SHIFT[] =
+{
+  //  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16
+      1,  1,  2,  2,  2,  2,  2,  2,  1,  2,  2,  2,  2,  2,  2,  1
+};
+
+#endif // EXCRYPT_DES_DATA_H_

+ 80 - 0
portal_of_flipper/xsm3/excrypt_parve.c

@@ -0,0 +1,80 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "excrypt.h"
+
+void ExCryptParveEcb(const uint8_t* key, const uint8_t* sbox, const uint8_t* input, uint8_t* output)
+{
+  uint8_t block[9];
+
+  memcpy(block, input, 8);
+  block[8] = block[0];
+
+  for (int i = 8; i > 0; i--)
+  {
+    for (int j = 0; j < 8; j++)
+    {
+      uint8_t x = key[j] + block[j] + i;
+      uint8_t y = sbox[x] + block[j + 1];
+      block[j + 1] = ROTL8(y, 1);
+    }
+
+    block[0] = block[8];
+  }
+
+  memcpy(output, block, 8);
+}
+
+void ExCryptParveCbcMac(const uint8_t* key, const uint8_t* sbox, const uint8_t* iv, const uint8_t* input, uint32_t input_size, uint8_t* output)
+{
+  uint64_t block;
+  uint64_t temp;
+  memcpy(&block, iv, 8);
+
+  if (input_size >= 8)
+  {
+    for (uint32_t i = 0; i < input_size / 8; i++)
+    {
+      memcpy(&temp, input + (i * 8), sizeof(temp));
+      block ^= temp;
+      ExCryptParveEcb(key, sbox, (uint8_t*)&block, (uint8_t*)&block);
+    }
+  }
+
+  memcpy(output, &block, 8);
+}
+
+void ExCryptChainAndSumMac(const uint32_t* cd, const uint32_t* ab, const uint32_t* input, uint32_t input_dwords, uint32_t* output)
+{
+  uint64_t out0 = 0;
+  uint64_t out1 = 0;
+
+  uint32_t ab0 = SWAP32(ab[0]) % 0x7FFFFFFF;
+  uint32_t ab1 = SWAP32(ab[1]) % 0x7FFFFFFF;
+  uint32_t cd0 = SWAP32(cd[0]) % 0x7FFFFFFF;
+  uint32_t cd1 = SWAP32(cd[1]) % 0x7FFFFFFF;
+
+  for (uint32_t i = 0; i < input_dwords / 2; i++)
+  {
+    out0 += (uint64_t)SWAP32(input[0]) * 0xE79A9C1;
+    out0 = (out0 % 0x7FFFFFFF) * ab0;
+    out0 += ab1;
+    out0 = out0 % 0x7FFFFFFF;
+
+    out1 += out0;
+
+    out0 = (uint64_t)(SWAP32(input[1]) + out0) * cd0;
+    out0 = (out0 % 0x7FFFFFFF) + cd1;
+    out0 = out0 % 0x7FFFFFFF;
+
+    out1 += out0;
+
+    input += 2;
+  }
+  out0 = SWAP32((out0 + ab1) % 0x7FFFFFFF);
+  out1 = SWAP32((out1 + cd1) % 0x7FFFFFFF);
+  memcpy(output, &out0,  sizeof(uint32_t));
+  memcpy(output+1, &out1,  sizeof(uint32_t));
+  // output[0] = SWAP32((out0 + ab1) % 0x7FFFFFFF);
+  // output[1] = SWAP32((out1 + cd1) % 0x7FFFFFFF);
+}

+ 9 - 0
portal_of_flipper/xsm3/excrypt_parve.h

@@ -0,0 +1,9 @@
+#ifndef EXCRYPT_PARVE_H_
+#define EXCRYPT_PARVE_H_
+// "Parve" functions, seem to be used during controller auth
+
+void ExCryptParveEcb(const uint8_t* key, const uint8_t* sbox, const uint8_t* input, uint8_t* output);
+void ExCryptParveCbcMac(const uint8_t* key, const uint8_t* sbox, const uint8_t* iv, const uint8_t* input, uint32_t input_size, uint8_t* output);
+void ExCryptChainAndSumMac(const uint32_t* cd, const uint32_t* ab, const uint32_t* input, uint32_t input_dwords, uint32_t* output);
+
+#endif // EXCRYPT_PARVE_H_

+ 156 - 0
portal_of_flipper/xsm3/excrypt_sha.c

@@ -0,0 +1,156 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "excrypt.h"
+
+// SHA1 code based on https://github.com/mohaps/TinySHA1
+
+void sha1_process_block(EXCRYPT_SHA_STATE* state)
+{
+  uint32_t w[80];
+  for (size_t i = 0; i < 16; i++) {
+    w[i] = ((uint32_t)state->buffer[i * 4 + 0] << 24);
+    w[i] |= ((uint32_t)state->buffer[i * 4 + 1] << 16);
+    w[i] |= ((uint32_t)state->buffer[i * 4 + 2] << 8);
+    w[i] |= ((uint32_t)state->buffer[i * 4 + 3]);
+  }
+  for (size_t i = 16; i < 80; i++) {
+    w[i] = ROTL32((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1);
+  }
+
+  uint32_t a = state->state[0];
+  uint32_t b = state->state[1];
+  uint32_t c = state->state[2];
+  uint32_t d = state->state[3];
+  uint32_t e = state->state[4];
+
+  for (int i = 0; i < 80; ++i) {
+    uint32_t f = 0;
+    uint32_t k = 0;
+
+    if (i < 20) {
+      f = (b & c) | (~b & d);
+      k = 0x5A827999;
+    }
+    else if (i < 40) {
+      f = b ^ c ^ d;
+      k = 0x6ED9EBA1;
+    }
+    else if (i < 60) {
+      f = (b & c) | (b & d) | (c & d);
+      k = 0x8F1BBCDC;
+    }
+    else {
+      f = b ^ c ^ d;
+      k = 0xCA62C1D6;
+    }
+    uint32_t temp = ROTL32(a, 5) + f + e + k + w[i];
+    e = d;
+    d = c;
+    c = ROTL32(b, 30);
+    b = a;
+    a = temp;
+  }
+
+  state->state[0] += a;
+  state->state[1] += b;
+  state->state[2] += c;
+  state->state[3] += d;
+  state->state[4] += e;
+}
+
+void sha1_process_byte(EXCRYPT_SHA_STATE* state, uint8_t octet)
+{
+  uint32_t offset = state->count++ & 0x3F;
+  state->buffer[offset] = octet;
+  if ((state->count & 0x3F) == 0)
+  {
+    sha1_process_block(state);
+  }
+}
+
+void ExCryptShaInit(EXCRYPT_SHA_STATE* state)
+{
+  state->count = 0;
+  state->state[0] = 0x67452301;
+  state->state[1] = 0xEFCDAB89;
+  state->state[2] = 0x98BADCFE;
+  state->state[3] = 0x10325476;
+  state->state[4] = 0xC3D2E1F0;
+}
+
+void ExCryptShaUpdate(EXCRYPT_SHA_STATE* state, const uint8_t* input, uint32_t input_size)
+{
+  for (uint32_t i = 0; i < input_size; i++)
+  {
+    sha1_process_byte(state, input[i]);
+  }
+}
+
+void ExCryptShaFinal(EXCRYPT_SHA_STATE* state, uint8_t* output, uint32_t output_size)
+{
+  (void)output_size;
+  uint64_t bit_count = (uint64_t)state->count * 8;
+
+  sha1_process_byte(state, 0x80);
+
+  if ((state->count & 0x3F) > 56)
+  {
+    while ((state->count & 0x3F) != 0)
+    {
+      sha1_process_byte(state, 0);
+    }
+    while ((state->count & 0x3F) < 56)
+    {
+      sha1_process_byte(state, 0);
+    }
+  }
+  else
+  {
+    while ((state->count & 0x3F) < 56)
+    {
+      sha1_process_byte(state, 0);
+    }
+  }
+
+  sha1_process_byte(state, 0);
+  sha1_process_byte(state, 0);
+  sha1_process_byte(state, 0);
+  sha1_process_byte(state, 0);
+
+  sha1_process_byte(state, (uint8_t)((bit_count >> 24) & 0xFF));
+  sha1_process_byte(state, (uint8_t)((bit_count >> 16) & 0xFF));
+  sha1_process_byte(state, (uint8_t)((bit_count >> 8) & 0xFF));
+  sha1_process_byte(state, (uint8_t)((bit_count) & 0xFF));
+
+  //sha1_process_block(state);
+  uint32_t result[5];
+  result[0] = SWAP32(state->state[0]);
+  result[1] = SWAP32(state->state[1]);
+  result[2] = SWAP32(state->state[2]);
+  result[3] = SWAP32(state->state[3]);
+  result[4] = SWAP32(state->state[4]);
+  memcpy(output, result, 0x14);
+}
+
+void ExCryptSha(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size,
+  const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size)
+{
+  EXCRYPT_SHA_STATE state[1];
+  ExCryptShaInit(state);
+
+  if (input1 && input1_size)
+  {
+    ExCryptShaUpdate(state, input1, input1_size);
+  }
+  if (input2 && input2_size)
+  {
+    ExCryptShaUpdate(state, input2, input2_size);
+  }
+  if (input3 && input3_size)
+  {
+    ExCryptShaUpdate(state, input3, input3_size);
+  }
+
+  ExCryptShaFinal(state, output, output_size);
+}

+ 18 - 0
portal_of_flipper/xsm3/excrypt_sha.h

@@ -0,0 +1,18 @@
+#ifndef EXCRYPT_SHA_H_
+#define EXCRYPT_SHA_H_
+// SHA1 hash & HMAC algorithm
+
+typedef struct _EXCRYPT_SHA_STATE
+{
+  uint32_t count;
+  uint32_t state[5];
+  uint8_t buffer[64];
+} EXCRYPT_SHA_STATE;
+
+void ExCryptShaInit(EXCRYPT_SHA_STATE* state);
+void ExCryptShaUpdate(EXCRYPT_SHA_STATE* state, const uint8_t* input, uint32_t input_size);
+void ExCryptShaFinal(EXCRYPT_SHA_STATE* state, uint8_t* output, uint32_t output_size);
+void ExCryptSha(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size,
+  const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size);
+
+#endif // EXCRYPT_SHA_H_

+ 157 - 0
portal_of_flipper/xsm3/usbdsec.c

@@ -0,0 +1,157 @@
+/*
+    usbdsec.c - part of libxsm3
+    Copyright (C) 2013 oct0xor
+    Copyright (C) 2022 InvoxiPlayGames
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include "excrypt.h"
+
+static uint8_t UsbdSecSboxData[256] __attribute__ ((aligned(4))) = {
+	0xB0, 0x3D, 0x9B, 0x70, 0xF3, 0xC7, 0x80, 0x60,
+	0x73, 0x9F, 0x6C, 0xC0, 0xF1, 0x3D, 0xBB, 0x40,
+	0xB3, 0xC8, 0x37, 0x14, 0xDF, 0x49, 0xDA, 0xD4,
+	0x48, 0x22, 0x78, 0x80, 0x6E, 0xCD, 0xE7, 0x00,
+	0x81, 0x86, 0x68, 0xE1, 0x5D, 0x7C, 0x54, 0x2C,
+	0x55, 0x7B, 0xEF, 0x48, 0x42, 0x7B, 0x3B, 0x68,
+	0xE3, 0xDB, 0xAA, 0xC0, 0x0F, 0xA9, 0x96, 0x20,
+	0x95, 0x05, 0x93, 0x94, 0x9A, 0xF6, 0xA3, 0x64,
+	0x5D, 0xCC, 0x76, 0x00, 0xE5, 0x08, 0x19, 0xE8,
+	0x8D, 0x29, 0xD7, 0x4C, 0x21, 0x91, 0x17, 0xF4,
+	0xBC, 0x6A, 0xB3, 0x80, 0x83, 0xC6, 0xD4, 0x90,
+	0x9B, 0xAE, 0x0E, 0xFE, 0x2E, 0x4A, 0xF2, 0x00,
+	0x73, 0x88, 0xD9, 0x40, 0x66, 0xC5, 0xD4, 0x08,
+	0x57, 0xB1, 0x89, 0x48, 0xDC, 0x54, 0xFC, 0x43,
+	0x6A, 0x26, 0x87, 0xB8, 0x09, 0x5F, 0xCE, 0x80,
+	0xE4, 0x0B, 0x05, 0x9C, 0x24, 0xF3, 0xDE, 0xE2,
+	0x3E, 0xEC, 0x38, 0x8A, 0xA2, 0x55, 0xA4, 0x50,
+	0x4E, 0x4B, 0xE9, 0x58, 0x7F, 0x9F, 0x7D, 0x80,
+	0x23, 0x0C, 0x4D, 0x80, 0x05, 0x44, 0x26, 0xB8,
+	0xE9, 0xD8, 0xBC, 0xE6, 0x76, 0x3A, 0x6E, 0xA4,
+	0x19, 0xDE, 0xC2, 0xD0, 0xC4, 0xBC, 0xC3, 0x5C,
+	0x59, 0xDF, 0x16, 0x46, 0x39, 0x70, 0xF4, 0xEE,
+	0x2D, 0x58, 0x5A, 0xA8, 0x17, 0x86, 0x6B, 0x60,
+	0x29, 0x58, 0x4D, 0xD2, 0x5F, 0x28, 0x7A, 0xD8,
+	0x8E, 0x79, 0xEA, 0x82, 0x94, 0x33, 0x31, 0x81,
+	0xD9, 0x22, 0xD5, 0x10, 0xDA, 0x92, 0xA0, 0x7D,
+	0x3D, 0xDA, 0xAC, 0x1C, 0xA2, 0x53, 0x31, 0xB8,
+	0x3C, 0x96, 0x52, 0x00, 0x82, 0x6B, 0x56, 0xA0,
+	0xD3, 0xC2, 0x40, 0xC7, 0x1B, 0x7F, 0xDC, 0x01,
+	0x72, 0x70, 0xB1, 0x8C, 0x01, 0x09, 0x09, 0x36,
+	0xFC, 0x97, 0xEA, 0xDE, 0xE3, 0x0D, 0xAE, 0x7E,
+	0xE3, 0x0D, 0xAE, 0x7E, 0x33, 0x69, 0x80, 0x40
+};
+
+static uint8_t UsbdSecPlainTextData[128] __attribute__ ((aligned(4))) = {
+	0xD1, 0xD2, 0xF2, 0x80, 0x6E, 0xBA, 0x0C, 0xC0,
+	0xB6, 0xC4, 0xC9, 0xD8, 0x61, 0x75, 0x1D, 0x1A,
+	0x3F, 0x95, 0x58, 0xBE, 0xD8, 0x0D, 0xE2, 0xC0,
+	0xD0, 0x21, 0x79, 0x20, 0x65, 0x2D, 0x99, 0x40,
+	0x3C, 0x96, 0x52, 0x00, 0x1B, 0x7F, 0xDC, 0x01,
+	0x82, 0x1C, 0x13, 0xD8, 0x33, 0x69, 0x80, 0x40,
+	0xFC, 0x97, 0xEA, 0xDE, 0x08, 0xEA, 0x14, 0xDC,
+	0xEB, 0x0F, 0x6A, 0x18, 0x6F, 0x78, 0x2C, 0xB0,
+	0xD3, 0xC2, 0x40, 0xC7, 0x82, 0x6B, 0x56, 0xA0,
+	0x19, 0x09, 0x36, 0xE0, 0x72, 0x70, 0xB1, 0x8C,
+	0xE3, 0x0D, 0xAE, 0x7E, 0x50, 0xA5, 0x2B, 0xE2,
+	0xC9, 0xAF, 0xC7, 0x70, 0x1C, 0x29, 0x80, 0x56,
+	0x24, 0xF0, 0x66, 0xFA, 0x02, 0x2B, 0x58, 0x98,
+	0x8F, 0xE4, 0xD1, 0x3C, 0x6E, 0x38, 0x2A, 0xFF,
+	0xB8, 0xFA, 0x35, 0xB0, 0x52, 0x49, 0xC5, 0xB4,
+	0x66, 0xFA, 0x47, 0x55, 0x6C, 0x8D, 0x40, 0x08
+};
+
+void UsbdSecXSM3AuthenticationCrypt(const uint8_t *key, const uint8_t *input, size_t length, uint8_t *output, uint8_t encrypt) {
+	EXCRYPT_DES3_STATE des;
+	uint64_t sk[3];
+	uint8_t iv[8];
+
+	// clear local variables
+    memset(iv, 0, sizeof(iv));
+	// run parity on the key
+	ExCryptDesParity(key, 0x10, (uint8_t *)sk);
+	sk[2] = sk[0];
+	// set the key in the state and run triple-des cbc en/decryption on it
+	ExCryptDes3Key(&des, sk);
+	ExCryptDes3Cbc(&des, input, length, output, iv, encrypt);
+}
+
+void UsbdSecXSM3AuthenticationMac(const uint8_t *key, uint8_t *salt, uint8_t *input, size_t length, uint8_t *output) {
+	EXCRYPT_DES3_STATE des3;
+	EXCRYPT_DES_STATE des;
+	uint64_t sk[3];
+	uint8_t iv[8];
+	uint8_t temp[8];
+	uint64_t input_temp;
+	size_t i;
+
+	// clear iv + temp value of stack junk
+	memset(iv, 0, sizeof(iv));
+	memset(temp, 0, sizeof(temp));
+	// run parity on the key
+	ExCryptDesParity(key, 0x10, (uint8_t *)sk);
+	sk[2] = sk[0];
+	// set the key in our initial des state
+	ExCryptDesKey(&des, (uint8_t *)&sk[0]);
+	// if we have a salt, encrypt it into the temp value
+	if (salt) {
+		memcpy(&input_temp, salt, sizeof(input_temp));
+		input_temp = SWAP64(SWAP64(input_temp) + 1);
+		memcpy(salt, &input_temp, sizeof(input_temp)); // no idea what this does
+		ExCryptDesEcb(&des, salt, temp, 1);
+	}
+	// for every 8 byte input block, xor the temp value with it and encrypt over itself
+	for (i = 0; i < length; i += 8) {
+		memcpy(&input_temp, input+i, sizeof(input_temp));
+		*(uint64_t *)temp ^= input_temp;
+		
+		ExCryptDesEcb(&des, temp, temp, 1);
+	}
+	// xor the highest bit of the temp value
+	temp[0] ^= 0x80;
+	// set the key and perform the final triple-des encryption
+	ExCryptDes3Key(&des3, sk);
+	ExCryptDes3Cbc(&des3, temp, 8, output, iv, 1);
+	// real kernel does the following, but the above works:
+	// XeCryptDesEcb(des_state_1, temp, temp, 1);
+	// XeCryptDesEcb(des_state_2, temp, temp, 0);
+	// XeCryptDesEcb(des_state_1, temp, output, 1);
+}
+
+void UsbdSecXSMAuthenticationAcr(const uint8_t *console_id, const uint8_t *input, const uint8_t *key, uint8_t *output) {
+	uint8_t block[8];
+	uint8_t iv[8];
+	uint8_t ab[8];
+	uint8_t cd[8];
+
+	// fill in the input block with the first 4 bytes of input data and the first 4 bytes of the console ID
+	// *(uint32_t *)block = *(uint32_t *)input;
+	// *(uint32_t *)(block + 4) = *(uint32_t *)console_id;
+	memcpy(block, input, 4);
+	memcpy(block+4, console_id, 4);
+	// run custom "parve" crypto algorithms. idk whar they do
+	ExCryptParveEcb(key, UsbdSecSboxData, input + 0x10, iv);
+	ExCryptParveEcb(key, UsbdSecSboxData, block, cd);
+	ExCryptParveCbcMac(key, UsbdSecSboxData, iv, UsbdSecPlainTextData, 0x80, ab);
+	ExCryptChainAndSumMac((uint32_t *)cd, (uint32_t *)ab, (uint32_t *)UsbdSecPlainTextData, 0x20, (uint32_t *)output);
+	uint64_t current;
+	memcpy(&current, output, sizeof(current));
+	current ^= *(uint64_t *)ab;
+	// *(uint64_t *)output ^= *(uint64_t *)ab;
+	memcpy(output, &current, sizeof(current));
+}

+ 29 - 0
portal_of_flipper/xsm3/usbdsec.h

@@ -0,0 +1,29 @@
+/*
+    usbdsec.h - part of libxsm3
+    Copyright (C) 2022 InvoxiPlayGames
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef USBDSEC_H_
+#define USBDSEC_H_
+
+#include <stddef.h>
+
+void UsbdSecXSM3AuthenticationCrypt(const uint8_t *key, const uint8_t *input, size_t length, uint8_t *output, uint8_t encrypt);
+void UsbdSecXSM3AuthenticationMac(const uint8_t *key, const uint8_t *salt, uint8_t *input, size_t length, uint8_t *output);
+void UsbdSecXSMAuthenticationAcr(const uint8_t *console_id, const uint8_t *input, const uint8_t *key, uint8_t *output);
+
+#endif // USBDSEC_H_

+ 264 - 0
portal_of_flipper/xsm3/xsm3.c

@@ -0,0 +1,264 @@
+/*
+    xsm3.c - part of libxsm3
+    Copyright (C) 2022 InvoxiPlayGames
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "xsm3.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "furi_hal_random.h"
+
+#include "excrypt.h"
+#include "usbdsec.h"
+
+// disable debugging by specifying XSM3_NO_DEBUGGING at compile time
+#ifndef XSM3_NO_DEBUGGING
+#define XSM3_printf printf
+#else
+#define XSM3_printf
+#endif  // XSM3_NO_DEBUGGING
+
+// constant variables
+uint8_t xsm3_id_data_ms_controller[0x1D] = {
+    0x49, 0x4B, 0x00, 0x00, 0x17, 0x41, 0x41, 0x41,
+    0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+    0x00, 0x00, 0x80, 0x02, 0x09, 0x12, 0x82, 0x28,
+    0x03, 0x00, 0x01, 0x01, 0x71};
+
+// static global keys from the keyvault (shared across every retail system)
+static const uint8_t xsm3_key_0x1D[0x10] = {
+    0xE3, 0x5B, 0xFB, 0x1C, 0xCD, 0xAD, 0x32, 0x5B,
+    0xF7, 0x0E, 0x07, 0xFD, 0x62, 0x3D, 0xA7, 0xC4};
+static const uint8_t xsm3_key_0x1E[0x10] = {
+    0x8F, 0x29, 0x08, 0x38, 0x0B, 0x5B, 0xFE, 0x68,
+    0x7C, 0x26, 0x46, 0x2A, 0x51, 0xF2, 0xBC, 0x19};
+
+// retail keys for generating 0x23/0x24 keys from console ID
+static const uint8_t xsm3_root_key_0x23[0x10] = {
+    0x82, 0x80, 0x78, 0x68, 0x3A, 0x52, 0x3A, 0x98,
+    0x10, 0xF4, 0x0C, 0x12, 0x70, 0x66, 0xDC, 0xBA};
+
+static const uint8_t xsm3_root_key_0x24[0x10] = {
+    0x66, 0x62, 0x1A, 0x78, 0xF8, 0x60, 0x9C, 0x8A,
+    0x26, 0x9A, 0x04, 0xAE, 0xD8, 0x5C, 0x1E, 0xC8};
+
+// response to give to the given challenge command
+uint8_t xsm3_challenge_response[0x30];
+// console id fetched from challenge init packet
+uint8_t xsm3_console_id[0x8];
+
+// buffer to store the first key fetched from the KV (0x23)
+static uint8_t xsm3_kv_2des_key_1[0x10];
+// buffer to store the second key fetched from the KV (0x24)
+static uint8_t xsm3_kv_2des_key_2[0x10];
+
+// temporary buffer for decrypting packets
+static uint8_t xsm3_decryption_buffer[0x30];
+// identification data for the current device
+static uint8_t xsm3_identification_data[0x20];
+// random data sent from the console during the challenge init stage
+static uint8_t xsm3_random_console_data[0x10];
+static uint8_t xsm3_random_console_data_enc[0x10];
+static uint8_t xsm3_random_console_data_swap[0x10];
+static uint8_t xsm3_random_console_data_swap_enc[0x10];
+// random data set by the controller during the challenge init stage
+static uint8_t xsm3_random_controller_data[0x10];
+// hash of the decrypted data sent by the controller during challenge init
+static uint8_t xsm3_challenge_init_hash[0x14];
+void xsm3_initialise_state() {
+    // set all variables to all zeroes
+    memset(xsm3_challenge_response, 0, sizeof(xsm3_challenge_response));
+    memset(xsm3_console_id, 0, sizeof(xsm3_console_id));
+    memset(xsm3_kv_2des_key_1, 0, sizeof(xsm3_kv_2des_key_1));
+    memset(xsm3_kv_2des_key_2, 0, sizeof(xsm3_kv_2des_key_2));
+    memset(xsm3_decryption_buffer, 0, sizeof(xsm3_decryption_buffer));
+    memset(xsm3_identification_data, 0, sizeof(xsm3_identification_data));
+    memset(xsm3_random_console_data, 0, sizeof(xsm3_random_console_data));
+    memset(xsm3_random_console_data_enc, 0, sizeof(xsm3_random_console_data_enc));
+    memset(xsm3_random_console_data_swap, 0, sizeof(xsm3_random_console_data_swap));
+    memset(xsm3_random_console_data_swap_enc, 0, sizeof(xsm3_random_console_data_swap_enc));
+    memset(xsm3_random_controller_data, 0, sizeof(xsm3_random_controller_data));
+    memset(xsm3_challenge_init_hash, 0, sizeof(xsm3_challenge_init_hash));
+}
+
+static uint8_t xsm3_calculate_checksum(const uint8_t* packet) {
+    // packet length in header doesn't include the header itself
+    uint8_t packet_length = packet[0x4] + 0x5;
+    uint8_t checksum = 0x00;
+    int i = 0;
+    // checksum is just a XOR over all packet bytes
+    for (i = 0x5; i < packet_length; i++) {
+        checksum ^= packet[i];
+    }
+    // last byte of the packet is the checksum
+    return checksum;
+}
+
+void xsm3_set_vid_pid(const uint8_t serial[0x0C], uint16_t vid, uint16_t pid) {
+    memcpy(xsm3_id_data_ms_controller + 6, serial, 0x0C);
+    uint8_t* id_data = xsm3_id_data_ms_controller;
+    // skip over the packet header
+    id_data += 0x5;
+    // vendor ID
+    memcpy(id_data + 0xf, &vid, sizeof(unsigned short));
+    // product ID
+    memcpy(id_data + 0x11, &pid, sizeof(unsigned short));
+    xsm3_id_data_ms_controller[0x1C] = xsm3_calculate_checksum(xsm3_id_data_ms_controller);
+}
+
+static bool xsm3_verify_checksum(const uint8_t* packet) {
+    // packet length in header doesn't include the header itself
+    uint8_t packet_length = packet[0x4] + 0x5;
+    // last byte of the packet is the checksum
+    return (xsm3_calculate_checksum(packet) == packet[packet_length]);
+}
+
+void xsm3_set_identification_data(const uint8_t id_data[0x1D]) {
+    // validate the checksum
+    if (!xsm3_verify_checksum(id_data)) {
+        XSM3_printf("[ Checksum failed when setting identification data! ]\n");
+    }
+
+    // skip over the packet header
+    id_data += 0x5;
+
+    // prepare the xsm3_identification_data buffer
+
+    // contains serial number (len: 0xC), unknown (len: 0x2) and the "category node" to use (len: 0x1)
+    memcpy(xsm3_identification_data, id_data, 0xF);
+    // vendor ID
+    memcpy(xsm3_identification_data + 0x10, id_data + 0xF, sizeof(unsigned short));
+    // product ID
+    memcpy(xsm3_identification_data + 0x12, id_data + 0x11, sizeof(unsigned short));
+    // unknown
+    memcpy(xsm3_identification_data + 0x14, id_data + 0x13, sizeof(unsigned char));
+    // unknown
+    memcpy(xsm3_identification_data + 0x15, id_data + 0x16, sizeof(unsigned char));
+    // unknown
+    memcpy(xsm3_identification_data + 0x16, id_data + 0x14, sizeof(unsigned short));
+}
+
+void xsm3_generate_kv_keys(const uint8_t console_id[0x8]) {
+    // make a sha-1 hash of the console id
+    uint8_t console_id_hash[0x14];
+    ExCryptSha(console_id, 0x8, NULL, 0, NULL, 0, console_id_hash, 0x14);
+    // encrypt it with the root keys for 1st party controllers
+    UsbdSecXSM3AuthenticationCrypt(xsm3_root_key_0x23, console_id_hash, 0x10, xsm3_kv_2des_key_1, 1);
+    UsbdSecXSM3AuthenticationCrypt(xsm3_root_key_0x24, console_id_hash + 0x4, 0x10, xsm3_kv_2des_key_2, 1);
+}
+
+void xsm3_do_challenge_init(uint8_t challenge_packet[0x22]) {
+    uint8_t incoming_packet_mac[0x8];
+    uint8_t response_packet_mac[0x8];
+    int i = 0;
+
+    // validate the checksum
+    if (!xsm3_verify_checksum(challenge_packet)) {
+        XSM3_printf("[ Checksum failed when validating challenge init! ]\n");
+    }
+
+    // decrypt the packet content using the static key from the keyvault
+    UsbdSecXSM3AuthenticationCrypt(xsm3_key_0x1D, challenge_packet + 0x5, 0x18, xsm3_decryption_buffer, 0);
+    // first 0x10 bytes are random data
+    memcpy(xsm3_random_console_data, xsm3_decryption_buffer, 0x10);
+    // next 0x8 bytes are from the console certificate
+    memcpy(xsm3_console_id, xsm3_decryption_buffer + 0x10, 0x8);
+    // last 4 bytes of the packet are the last 4 bytes of the MAC
+    UsbdSecXSM3AuthenticationMac(xsm3_key_0x1E, NULL, challenge_packet + 5, 0x18, incoming_packet_mac);
+    // validate the MAC
+    if (memcmp(incoming_packet_mac + 4, challenge_packet + 0x5 + 0x18, 0x4) != 0) {
+        XSM3_printf("[ MAC failed when validating challenge init! ]\n");
+    }
+    xsm3_generate_kv_keys(xsm3_console_id);
+
+    // the random value is swapped at an 8 byte boundary
+    memcpy(xsm3_random_console_data_swap, xsm3_random_console_data + 0x8, 0x8);
+    memcpy(xsm3_random_console_data_swap + 0x8, xsm3_random_console_data, 0x8);
+    // and then encrypted - the regular value encrypted with key 1, the swapped value encrypted with key 2
+    UsbdSecXSM3AuthenticationCrypt(xsm3_kv_2des_key_1, xsm3_random_console_data, 0x10, xsm3_random_console_data_enc, 1);
+    UsbdSecXSM3AuthenticationCrypt(xsm3_kv_2des_key_2, xsm3_random_console_data_swap, 0x10, xsm3_random_console_data_swap_enc, 1);
+
+    // generate random data
+    for (i = 0; i < 0x10; i++) {
+        xsm3_random_controller_data[i] = furi_hal_random_get() & 0xFF;
+    }
+
+    // clear response buffers
+    memset(xsm3_challenge_response, 0, sizeof(xsm3_challenge_response));
+    memset(xsm3_decryption_buffer, 0, sizeof(xsm3_decryption_buffer));
+    // set header and packet length of challenge response
+    xsm3_challenge_response[0] = 0x49;  // packet magic
+    xsm3_challenge_response[1] = 0x4C;
+    xsm3_challenge_response[4] = 0x28;  // packet length
+    // copy random controller, random console data to the encryption buffer
+    memcpy(xsm3_decryption_buffer, xsm3_random_controller_data, 0x10);
+    memcpy(xsm3_decryption_buffer + 0x10, xsm3_random_console_data, 0x10);
+    // save the sha1 hash of the decrypted contents for later
+    ExCryptSha(xsm3_decryption_buffer, 0x20, NULL, 0, NULL, 0, xsm3_challenge_init_hash, 0x14);
+
+    // encrypt challenge response packet using the encrypted random key
+    UsbdSecXSM3AuthenticationCrypt(xsm3_random_console_data_enc, xsm3_decryption_buffer, 0x20, xsm3_challenge_response + 0x5, 1);
+    // calculate MAC using the encrypted swapped random key and use it to calculate ACR
+    UsbdSecXSM3AuthenticationMac(xsm3_random_console_data_swap_enc, NULL, xsm3_challenge_response + 0x5, 0x20, response_packet_mac);
+    // calculate ACR and append to the end of the xsm3_challenge_response
+    UsbdSecXSMAuthenticationAcr(xsm3_console_id, xsm3_identification_data, response_packet_mac, xsm3_challenge_response + 0x5 + 0x20);
+    // calculate the checksum for the response packet
+    xsm3_challenge_response[0x5 + 0x28] = xsm3_calculate_checksum(xsm3_challenge_response);
+
+    // the console random value changes slightly after this point
+    memcpy(xsm3_random_console_data, xsm3_random_controller_data + 0xC, 0x4);
+    memcpy(xsm3_random_console_data + 0x4, xsm3_random_console_data + 0xC, 0x4);
+}
+
+void xsm3_do_challenge_verify(uint8_t challenge_packet[0x16]) {
+    uint8_t incoming_packet_mac[0x8];
+
+    // validate the checksum
+    if (!xsm3_verify_checksum(challenge_packet)) {
+        XSM3_printf("[ Checksum failed when validating challenge verify! ]\n");
+    }
+
+    // decrypt the packet using the controller generated random value
+    UsbdSecXSM3AuthenticationCrypt(xsm3_random_controller_data, challenge_packet + 0x5, 0x8, xsm3_decryption_buffer, 0);
+    // replace part of our random encryption value with the decrypted buffer
+    memcpy(xsm3_random_console_data + 0x8, xsm3_decryption_buffer, 0x8);
+
+    // calculate the MAC of the incoming packet
+    UsbdSecXSM3AuthenticationMac(xsm3_challenge_init_hash, xsm3_random_console_data, challenge_packet + 0x5, 0x8, incoming_packet_mac);
+    // validate the MAC
+    if (memcmp(incoming_packet_mac, challenge_packet + 0x5 + 0x8, 0x8) != 0) {
+        XSM3_printf("[ MAC failed when validating challenge verify! ]\n");
+    }
+    // clear response buffers
+    memset(xsm3_challenge_response, 0, sizeof(xsm3_challenge_response));
+    memset(xsm3_decryption_buffer, 0, sizeof(xsm3_decryption_buffer));
+    // set header and packet length of challenge response
+    xsm3_challenge_response[0] = 0x49;  // packet magic
+    xsm3_challenge_response[1] = 0x4C;
+    xsm3_challenge_response[4] = 0x10;  // packet length
+    // calculate the ACR value and encrypt it into the outgoing packet using the encrypted random
+    UsbdSecXSMAuthenticationAcr(xsm3_console_id, xsm3_identification_data, xsm3_random_console_data + 0x8, xsm3_decryption_buffer);
+    UsbdSecXSM3AuthenticationCrypt(xsm3_random_console_data_enc, xsm3_decryption_buffer, 0x8, xsm3_challenge_response + 0x5, 1);
+    // calculate the MAC of the encrypted packet and append it to the end
+    UsbdSecXSM3AuthenticationMac(xsm3_random_console_data_swap_enc, xsm3_random_console_data, xsm3_challenge_response + 0x5, 0x8, xsm3_challenge_response + 0x5 + 0x8);
+    // calculate the checksum for the response packet
+    xsm3_challenge_response[0x5 + 0x10] = xsm3_calculate_checksum(xsm3_challenge_response);
+}

+ 54 - 0
portal_of_flipper/xsm3/xsm3.h

@@ -0,0 +1,54 @@
+/*
+    xsm3.h - part of libxsm3
+    Copyright (C) 2022 InvoxiPlayGames
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef XSM3_H_
+#define XSM3_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Identification data taken from an official wired controller. (Serial number is static.)
+extern uint8_t xsm3_id_data_ms_controller[0x1D];
+
+// The response data from the previously completed challenge.
+extern uint8_t xsm3_challenge_response[0x30];
+
+// The console ID fetched from the console after request 0x82.
+extern uint8_t xsm3_console_id[0x8];
+
+// Clears the state of the XSM3 internal variables.
+void xsm3_initialise_state();
+
+// Sets the identification data to use.
+void xsm3_set_identification_data(const uint8_t id_data[0x1D]);
+
+// Initialises the XSM3 state using information from the challenge init packet (0x82) and places a response in xsm3_challenge_response.
+void xsm3_do_challenge_init(uint8_t challenge_packet[0x22]);
+
+// Completes a verify challenge passed from request 0x87 and places the response data in xsm3_challenge_response.
+void xsm3_do_challenge_verify(uint8_t challenge_packet[0x16]);
+
+void xsm3_set_vid_pid(const uint8_t serial[0x0C], uint16_t vid, uint16_t pid);
+#ifdef __cplusplus
+}
+#endif
+#endif // XSM3_H_