Xbox 360 - No Dongle Authentication (#1426)

* Solid connection?

* Add a small wait after we reset the port

* Moving up to latest as this fixes a lot of reset logic

* Moving libraries over to jfedor's set, standard does not work for SDK 2.1.1

* Switching Pico and TinyUSB to specific versions via hid-remapper

* Working?

* Move TX to PIO0:2

Move NeoPico to PIO0:1

* Moving state machines back to 0.7.11 alignment

* And a pause/wait for core1 to be ready before running it solves my last issue

* Removing core1 reset entirely, remove i2c hangup if display is not present during web config

* Reverting / clean some code, restore tusb to our old config, update aux and remove comments for PR

* Xbox 360 authentication - Santroller + XSM3

* Adding the missing files to the make

* ANd the missing XSM3 files
This commit is contained in:
Luke A
2025-07-15 22:17:12 -04:00
committed by GitHub
parent 8c4ac5c7b5
commit ed3477f7a2
19 changed files with 1256 additions and 17 deletions

View File

@@ -172,6 +172,11 @@ src/addonmanager.cpp
src/playerleds.cpp
src/drivers/shared/xinput_host.cpp
src/drivers/shared/xgip_protocol.cpp
src/drivers/shared/xsm3/excrypt_des.c
src/drivers/shared/xsm3/excrypt_parve.c
src/drivers/shared/xsm3/excrypt_sha.c
src/drivers/shared/xsm3/usbdsec.c
src/drivers/shared/xsm3/xsm3.c
src/drivers/astro/AstroDriver.cpp
src/drivers/egret/EgretDriver.cpp
src/drivers/hid/HIDDriver.cpp
@@ -301,6 +306,7 @@ headers
headers/addons
headers/configs
headers/drivers
headers/drivers/shared
headers/events
headers/interfaces
headers/interfaces/i2c

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -56,6 +56,7 @@ typedef struct {
class XInputAuth : public GPAuthDriver {
public:
XInputAuth(InputModeAuthType inType) { authType = inType; }
virtual void initialize();
virtual bool available();
void process();

View File

@@ -30,13 +30,14 @@ public:
virtual const uint8_t * get_descriptor_device_qualifier_cb();
virtual uint16_t GetJoystickMidValue();
virtual USBListener * get_usb_auth_listener();
bool getAuthEnabled();
bool getAuthSent();
private:
uint8_t last_report[CFG_TUD_ENDPOINT0_SIZE] = { };
XInputReport xinputReport;
XInputAuth * xAuthDriver;
uint8_t featureBuffer[XINPUT_OUT_SIZE];
uint8_t tud_buffer[64];
bool xAuthSent;
};
#endif

View File

@@ -197,7 +197,7 @@ void ButtonLayoutScreen::generateHeader() {
break;
case INPUT_MODE_XINPUT:
statusBar += "X";
if(((XInputDriver*)DriverManager::getInstance().getDriver())->getAuthEnabled() == true )
if(((XInputDriver*)DriverManager::getInstance().getDriver())->getAuthSent() == true )
statusBar += "B360";
else
statusBar += "INPUT";

View File

@@ -0,0 +1,198 @@
#include "xsm3/excrypt.h"
#include "xsm3/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;
}

View File

@@ -0,0 +1,80 @@
#include <stdlib.h>
#include <string.h>
#include "xsm3/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);
}

View File

@@ -0,0 +1,155 @@
#include <stdlib.h>
#include <string.h>
#include "xsm3/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)
{
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);
}

View File

@@ -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 "xsm3/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;
int 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));
}

View File

@@ -0,0 +1,269 @@
/*
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/xsm3.h"
//#include <avr/pgmspace.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
//#include "xsm3/config.h"
#include "xsm3/excrypt.h"
#include "xsm3/usbdsec.h"
#define XSM3_NO_DEBUGGING true
// 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
srand(time(NULL));
for (i = 0; i < 0x10; i++) {
xsm3_random_controller_data[i] = rand() & 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);
}

View File

@@ -2,24 +2,66 @@
#include "drivers/xinput/XInputAuthUSBListener.h"
#include "peripheralmanager.h"
#include "xsm3/xsm3.h"
void XInputAuth::initialize() {
if ( available() ) {
listener = new XInputAuthUSBListener();
if ( !available() ) {
return;
}
if ( available() ) {
listener = nullptr;
if (authType == InputModeAuthType::INPUT_MODE_AUTH_TYPE_USB ) {
listener = new XInputAuthUSBListener();
xinputAuthData.xinputState = auth_idle_state;
xinputAuthData.authCompleted = false;
((XInputAuthUSBListener*)listener)->setup();
((XInputAuthUSBListener*)listener)->setAuthData(&xinputAuthData);
} else {
// Generate a serial number from the pico's unique ID
uint8_t serial[0x0C];
pico_unique_board_id_t id;
pico_get_unique_board_id(&id);
for(int i = 0; i < 0x0C; i++) {
serial[i] = 'A' + (id.id[i]%25); // some alphanumeric from 'A' to 'Z'
}
xsm3_set_vid_pid(serial, 0x045E, 0x028E);
xsm3_initialise_state();
xsm3_set_identification_data(xsm3_id_data_ms_controller);
xinputAuthData.xinputState = auth_idle_state;
xinputAuthData.authCompleted = false;
xinputAuthData.dongle_ready = true;
memcpy(xinputAuthData.dongleSerial, xsm3_id_data_ms_controller, X360_AUTHLEN_DONGLE_SERIAL);
}
}
bool XInputAuth::available() {
return PeripheralManager::getInstance().isUSBEnabled(0);
// Move options over to their own ps4 data structure or gamepad?
if ( authType == InputModeAuthType::INPUT_MODE_AUTH_TYPE_USB ) {
return (PeripheralManager::getInstance().isUSBEnabled(0));
} else {
return true;
}
}
void XInputAuth::process() {
((XInputAuthUSBListener*)listener)->process();
}
// Use USB dongle
if ( authType == InputModeAuthType::INPUT_MODE_AUTH_TYPE_USB ) {
((XInputAuthUSBListener*)listener)->process(); // send to USB dongle
return;
}
// Process Xbox360 Console Request
if ( xinputAuthData.xinputState == GPAuthState::send_auth_console_to_dongle ) {
if ( xinputAuthData.passthruBufferID == XSM360AuthRequest::XSM360_INIT_AUTH ) {
xsm3_do_challenge_init(xinputAuthData.passthruBuffer);
memcpy(xinputAuthData.passthruBuffer, xsm3_challenge_response, X360_AUTHLEN_DONGLE_INIT);
xinputAuthData.passthruBufferLen = X360_AUTHLEN_DONGLE_INIT;
xinputAuthData.xinputState = GPAuthState::send_auth_dongle_to_console;
} else if ( xinputAuthData.passthruBufferID == XSM360AuthRequest::XSM360_VERIFY_AUTH ) {
xsm3_do_challenge_verify(xinputAuthData.passthruBuffer);
memcpy(xinputAuthData.passthruBuffer, xsm3_challenge_response, X360_AUTHLEN_CHALLENGE);
xinputAuthData.passthruBufferLen = X360_AUTHLEN_CHALLENGE;
xinputAuthData.xinputState = GPAuthState::send_auth_dongle_to_console;
}
}
}

View File

@@ -151,18 +151,17 @@ void XInputDriver::initialize() {
};
xAuthDriver = nullptr;
xAuthSent = false;
}
void XInputDriver::initializeAux() {
xAuthDriver = nullptr;
// AUTH DRIVER NON-FUNCTIONAL FOR NOW
GamepadOptions & gamepadOptions = Storage::getInstance().getGamepadOptions();
if ( gamepadOptions.xinputAuthType == InputModeAuthType::INPUT_MODE_AUTH_TYPE_USB ) {
xAuthDriver = new XInputAuth();
if ( xAuthDriver->available() ) {
xAuthDriver->initialize();
xinputAuthData = xAuthDriver->getAuthData();
}
xAuthDriver = new XInputAuth(gamepadOptions.xinputAuthType);
if ( xAuthDriver->available() ) {
xAuthDriver->initialize();
xinputAuthData = xAuthDriver->getAuthData();
}
}
@@ -173,8 +172,8 @@ USBListener * XInputDriver::get_usb_auth_listener() {
return nullptr;
}
bool XInputDriver::getAuthEnabled() {
return (xAuthDriver != nullptr);
bool XInputDriver::getAuthSent() {
return xAuthSent;
}
bool XInputDriver::process(Gamepad * gamepad) {
@@ -315,6 +314,7 @@ bool XInputDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_co
}
len = X360_AUTHLEN_DONGLE_SERIAL;
memcpy(tud_buffer, xinputAuthData->dongleSerial, len);
xAuthSent = true; // triggers on serial request but this is only for visual flair
break;
case XSM360_RESPOND_CHALLENGE:
if ( xinputAuthData->xinputState == GPAuthState::send_auth_dongle_to_console ) {

View File

@@ -80,7 +80,7 @@ export default {
'xbone-mode-text':
'<span>INFO:</span> Xbox One requires a USB host connection and USB dongle to properly authenticate in Xbox One mode.',
'xinput-mode-text':
'<span>INFO:</span> Xinput will work on a PC without any authentication. If you would like to on a Xbox 360 please choose USB authentication and attach an appropriate dongle.',
'<span>INFO:</span> XInput mode will work on a retail Xbox 360 console without a dongle. Only select USB if you would like to use an external dongle for authentication.',
'hotkey-settings-label': 'Hotkey Settings',
'hotkey-settings-sub-header':
'The <strong>Fn</strong> slider provides a mappable Function button in the <link_pinmap>Pin Mapping</link_pinmap> page. By selecting the <strong>Fn</strong> slider option, the Function button must be held along with the selected hotkey settings. <br /> Additionally, select <strong>None</strong> from the dropdown to unassign any button.',