/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include #include "xenia/base/logging.h" #include "xenia/base/platform.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "third_party/crypto/TinySHA1.hpp" #include "third_party/crypto/des/des.cpp" #include "third_party/crypto/des/des.h" #include "third_party/crypto/des/des3.h" #include "third_party/crypto/des/descbc.h" #include "third_party/crypto/sha256.cpp" #include "third_party/crypto/sha256.h" #include "xenia/kernel/xboxkrnl/xecrypt_rsa.h" extern "C" { #include "third_party/FFmpeg/libavutil/md5.h" #include "third_party/FFmpeg/libavutil/sha512.h" #include "third_party/aes_128/aes.h" } namespace xe { namespace kernel { namespace xboxkrnl { struct XECRYPT_RC4_STATE { uint8_t S[256]; // 0x0 uint8_t i; // 0x100 uint8_t j; // 0x101 }; static_assert_size(XECRYPT_RC4_STATE, 0x102); struct XECRYPT_SHA_STATE { xe::be count; // 0x0 xe::be state[5]; // 0x4 uint8_t buffer[64]; // 0x18 }; static_assert_size(XECRYPT_SHA_STATE, 0x58); // TODO: Size of this struct hasn't been confirmed yet. struct XECRYPT_SHA256_STATE { xe::be count; // 0x0 xe::be state[8]; // 0x4 uint8_t buffer[64]; // 0x24 }; // TODO: Size of this struct hasn't been confirmed yet. struct XECRYPT_SHA512_STATE { xe::be count; // 0x0 xe::be state[8]; // 0x8 uint8_t buffer[128]; // 0x48 }; struct SHA512_STATE { uint8_t digest_len; uint64_t count; uint8_t buffer[128]; uint64_t state[8]; }; // TODO: Size of this struct hasn't been confirmed yet. struct XECRYPT_MD5_STATE { xe::be count; xe::be state[4]; uint8_t buffer[64]; }; struct MD5_STATE { uint64_t len; uint8_t block[64]; uint32_t ABCD[4]; }; struct XECRYPT_RSA { xe::be size; // size of modulus in 8 byte units xe::be public_exponent; xe::be pad_8; // followed by modulus, followed by any private-key data }; static_assert_size(XECRYPT_RSA, 0x10); struct XECRYPT_SIG { xe::be aqwPad[0x1C]; xe::be One; uint8_t Salt[0xA]; uint8_t Hash[0x14]; xe::be End; }; static_assert_size(XECRYPT_SIG, 0x100); // size 256 struct XECRYPT_DES_STATE { uint32_t keytab[16][2]; }; struct XECRYPT_DES3_STATE { XECRYPT_DES_STATE des_state[3]; }; struct XECRYPT_AES_STATE { uint8_t keytabenc[11][4][4]; // 0x0 uint8_t keytabdec[11][4][4]; // 0xB0 }; static_assert_size(XECRYPT_AES_STATE, 0x160); // Keys // TODO: Array of keys we need // Console Serial Number Key - unimplemented static constexpr uint8_t key14[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '1', '0'}; // string // Retail key 0x19 static constexpr uint8_t key19[] = {0xE1, 0xBC, 0x15, 0x9C, 0x73, 0xB1, 0xEA, 0xE9, 0xAB, 0x31, 0x70, 0xF3, 0xAD, 0x47, 0xEB, 0xF3}; // Pirs Public Key struct XECRYPT_RSAPUB_2048 { XECRYPT_RSA Rsa; xe::be aqwM[0x20]; }; static_assert_size(XECRYPT_RSAPUB_2048, 0x110); static constexpr uint8_t key39[] = { // pirs_retail_public 0x00, 0x00, 0x00, 0x20, // cqw 0x00, 0x00, 0x00, 0x03, // dwPubExp 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // qwReserved // aqwM 0xE6, 0x3B, 0x32, 0xB2, 0x8D, 0x9E, 0x9E, 0xE7, 0x9D, 0xFC, 0x5C, 0x72, 0x41, 0x94, 0x58, 0x47, 0xDE, 0x0D, 0x18, 0x40, 0x72, 0xD6, 0xE3, 0x46, 0x8E, 0xBA, 0x8E, 0xBC, 0x1A, 0x90, 0xAC, 0x20, 0xBA, 0x03, 0x85, 0xB5, 0x1A, 0x3E, 0x25, 0xF9, 0xA6, 0x58, 0xEB, 0xB6, 0xA3, 0xC4, 0xA3, 0xEE, 0xB2, 0xB0, 0xAE, 0x97, 0x69, 0xEB, 0xFE, 0x71, 0xFC, 0x02, 0xAB, 0x77, 0xBA, 0xC8, 0xE6, 0x74, 0xE6, 0x7C, 0x63, 0x0E, 0xAF, 0x4C, 0xF7, 0xE7, 0x11, 0x4A, 0x80, 0x24, 0x72, 0x05, 0x7A, 0x63, 0xD0, 0xF8, 0x91, 0x02, 0xA6, 0xE7, 0x7D, 0x77, 0xC5, 0xA7, 0x9B, 0x08, 0x11, 0x2E, 0xA0, 0x64, 0x45, 0x60, 0x46, 0xBC, 0x36, 0xE1, 0x17, 0x71, 0xBE, 0x66, 0x49, 0x2F, 0xAE, 0x20, 0xA4, 0x76, 0x9C, 0x27, 0x51, 0xCF, 0x4B, 0x34, 0x7A, 0x35, 0xBC, 0xA4, 0xAA, 0x1C, 0x47, 0x4B, 0xF4, 0x97, 0x22, 0x4E, 0x13, 0x24, 0xD3, 0xC1, 0x57, 0xDF, 0x4D, 0x84, 0xB9, 0x18, 0x97, 0x99, 0xAC, 0x00, 0xB3, 0x3D, 0x03, 0x25, 0x60, 0xC8, 0x7A, 0x59, 0xFE, 0x48, 0xFF, 0x28, 0x3D, 0x10, 0xBB, 0x9E, 0x09, 0x06, 0x2A, 0x61, 0x20, 0x2C, 0xF8, 0x72, 0xEB, 0x87, 0xE6, 0xD1, 0xFB, 0xB3, 0x66, 0xFC, 0x4A, 0x02, 0xAE, 0xD4, 0xD8, 0x37, 0xCF, 0xA6, 0x32, 0x25, 0x79, 0x36, 0x0E, 0xF4, 0xED, 0x19, 0xA2, 0x10, 0x27, 0x96, 0x2F, 0x9F, 0xA9, 0x3D, 0xA4, 0x37, 0x30, 0x11, 0x51, 0x83, 0xBD, 0xF7, 0xC7, 0xE5, 0xCE, 0xAA, 0xEC, 0xDE, 0x48, 0xA0, 0x84, 0xF7, 0xB0, 0xF6, 0x4B, 0x8E, 0xF0, 0x89, 0xBD, 0x47, 0x7C, 0x90, 0xDD, 0x88, 0x12, 0x17, 0x40, 0xD2, 0x4E, 0xA6, 0xC6, 0x11, 0x04, 0x1B, 0x57, 0xA8, 0x68, 0xB4, 0x61, 0xF4, 0x1B, 0xC6, 0x8B, 0xE8, 0xD9, 0x20, 0xF2, 0x05, 0xE0, 0x70}; const static std::map> key_vault = { {0x14, key14}, {0x19, key19}, {0x39, key39}, }; enum X_KEY_INDEX : uint32_t { MANUFACTURING_MODE = 0x0, ALTERNATE_KEY_VAULT = 0x1, RESTRICTED_PRIVILEGES_FLAGS = 0x2, RESERVED_BYTE3 = 0x3, ODD_FEATURES = 0x4, ODD_AUTHTYPE = 0x5, RESTRICTED_HVEXT_LOADER = 0x6, POLICY_FLASH_SIZE = 0x7, POLICY_BUILTIN_USBMU_SIZE = 0x8, RESERVED_DWORD4 = 0x9, RESTRICTED_PRIVILEGES = 0xA, RESERVED_QWORD2 = 0xB, RESERVED_QWORD3 = 0xC, RESERVED_QWORD4 = 0xD, RESERVED_KEY1 = 0xE, RESERVED_KEY2 = 0xF, RESERVED_KEY3 = 0x10, RESERVED_KEY4 = 0x11, RESERVED_RANDOM_KEY1 = 0x12, RESERVED_RANDOM_KEY2 = 0x13, CONSOLE_SERIAL_NUMBER = 0x14, MOBO_SERIAL_NUMBER = 0x15, GAME_REGION = 0x16, CONSOLE_OBFUSCATION_KEY = 0x17, KEY_OBFUSCATION_KEY = 0x18, ROAMABLE_OBFUSCATION_KEY = 0x19, DVD_KEY = 0x1A, PRIMARY_ACTIVATION_KEY = 0x1B, SECONDARY_ACTIVATION_KEY = 0x1C, GLOBAL_DEVICE_2DES_KEY1 = 0x1D, GLOBAL_DEVICE_2DES_KEY2 = 0x1E, WIRELESS_CONTROLLER_MS_2DES_KEY1 = 0x1F, WIRELESS_CONTROLLER_MS_2DES_KEY2 = 0x20, WIRED_WEBCAM_MS_2DES_KEY1 = 0x21, WIRED_WEBCAM_MS_2DES_KEY2 = 0x22, WIRED_CONTROLLER_MS_2DES_KEY1 = 0x23, WIRED_CONTROLLER_MS_2DES_KEY2 = 0x24, MEMORY_UNIT_MS_2DES_KEY1 = 0x25, MEMORY_UNIT_MS_2DES_KEY2 = 0x26, OTHER_XSM3_DEVICE_MS_2DES_KEY1 = 0x27, OTHER_XSM3_DEVICE_MS_2DES_KEY2 = 0x28, WIRELESS_CONTROLLER_3P_2DES_KEY1 = 0x29, WIRELESS_CONTROLLER_3P_2DES_KEY2 = 0x2A, WIRED_WEBCAM_3P_2DES_KEY1 = 0x2B, WIRED_WEBCAM_3P_2DES_KEY2 = 0x2C, WIRED_CONTROLLER_3P_2DES_KEY1 = 0x2D, WIRED_CONTROLLER_3P_2DES_KEY2 = 0x2E, MEMORY_UNIT_3P_2DES_KEY1 = 0x2F, MEMORY_UNIT_3P_2DES_KEY2 = 0x30, OTHER_XSM3_DEVICE_3P_2DES_KEY1 = 0x31, OTHER_XSM3_DEVICE_3P_2DES_KEY2 = 0x32, CONSOLE_PRIVATE_KEY = 0x33, XEIKA_PRIVATE_KEY = 0x34, CARDEA_PRIVATE_KEY = 0x35, CONSOLE_CERTIFICATE = 0x36, XEIKA_CERTIFICATE = 0x37, CARDEA_CERTIFICATE = 0x38, MAX_KEY_INDEX = 0x39, // constant keys CONSTANT_PIRS_KEY = 0x39, CONSTANT_ALT_MASTER_KEY = 0x3A, CONSTANT_ALT_LIVE_KEY = 0x3B, CONSTANT_MASTER_KEY = 0x3C, CONSTANT_LIVE_KEY = 0x3D, CONSTANT_XB1_GREEN_KEY = 0x3E, CONSTANT_SATA_DISK_SECURITY_KEY = 0x3F, CONSTANT_DEVICE_REVOCATION_KEY = 0x40, CONSTANT_XMACS_KEY = 0x41, CONSTANT_REVOCATION_LIST_NONCE = 0x42, CONSTANT_CROSS_PLATFORM_SYSLINK_KEY = 0x43, // special keys SPECIAL_KEY_VAULT_SIGNATURE = 0x44, SPECIAL_SECROM_DIGEST = 0x45, SPECIAL_SECDATA = 0x46, SPECIAL_DVD_FIRMWARE_KEY = 0x47, SPECIAL_DEBUG_UNLOCK = 0x48, MAX_CONSTANT_INDEX = 0x49, // title keys TITLE_KEYS_BASE = 0xE0, TITLE_KEYS_LIMIT = 0xE8, TITLE_KEYS_RESET = 0xF0, // secured keys SECURED_DATA_BASE = 0x1000, SECURED_DATA_LIMIT = 0x2000, }; // https://github.com/emoose/ExCrypt/blob/master/src/exkeys.cpp#L7 std::map> X_Key_Properties = { {MANUFACTURING_MODE, {0x8, 0x1}}, {ALTERNATE_KEY_VAULT, {0x9, 0x1}}, {RESTRICTED_PRIVILEGES_FLAGS, {0xA, 0x1}}, {RESERVED_BYTE3, {0xB, 0x1}}, {ODD_FEATURES, {0xC, 0x2}}, {ODD_AUTHTYPE, {0xE, 0x2}}, {RESTRICTED_HVEXT_LOADER, {0x10, 0x4}}, {POLICY_FLASH_SIZE, {0x14, 0x4}}, {POLICY_BUILTIN_USBMU_SIZE, {0x18, 0x4}}, {RESERVED_DWORD4, {0x1C, 0x4}}, {RESTRICTED_PRIVILEGES, {0x20, 0x8}}, {RESERVED_QWORD2, {0x28, 0x8}}, {RESERVED_QWORD3, {0x30, 0x8}}, {RESERVED_QWORD4, {0x38, 0x8}}, {RESERVED_KEY1, {0x40, 0x10}}, {RESERVED_KEY2, {0x50, 0x10}}, {RESERVED_KEY3, {0x60, 0x10}}, {RESERVED_KEY4, {0x70, 0x10}}, {RESERVED_RANDOM_KEY1, {0x80, 0x10}}, {RESERVED_RANDOM_KEY2, {0x90, 0x10}}, {CONSOLE_SERIAL_NUMBER, {0xA0, 0xC}}, {MOBO_SERIAL_NUMBER, {0xAC, 0xC}}, {GAME_REGION, {0xB8, 0x2}}, // 6 bytes padding {CONSOLE_OBFUSCATION_KEY, {0xC0, 0x10}}, {KEY_OBFUSCATION_KEY, {0xD0, 0x10}}, {ROAMABLE_OBFUSCATION_KEY, {0xE0, 0x10}}, {DVD_KEY, {0xF0, 0x10}}, {PRIMARY_ACTIVATION_KEY, {0x100, 0x18}}, {SECONDARY_ACTIVATION_KEY, {0x118, 0x10}}, {GLOBAL_DEVICE_2DES_KEY1, {0x128, 0x10}}, {GLOBAL_DEVICE_2DES_KEY2, {0x138, 0x10}}, {WIRELESS_CONTROLLER_MS_2DES_KEY1, {0x148, 0x10}}, {WIRELESS_CONTROLLER_MS_2DES_KEY2, {0x158, 0x10}}, {WIRED_WEBCAM_MS_2DES_KEY1, {0x168, 0x10}}, {WIRED_WEBCAM_MS_2DES_KEY2, {0x178, 0x10}}, {WIRED_CONTROLLER_MS_2DES_KEY1, {0x188, 0x10}}, {WIRED_CONTROLLER_MS_2DES_KEY2, {0x198, 0x10}}, {MEMORY_UNIT_MS_2DES_KEY1, {0x1A8, 0x10}}, {MEMORY_UNIT_MS_2DES_KEY2, {0x1B8, 0x10}}, {OTHER_XSM3_DEVICE_MS_2DES_KEY1, {0x1C8, 0x10}}, {OTHER_XSM3_DEVICE_MS_2DES_KEY2, {0x1D8, 0x10}}, {WIRELESS_CONTROLLER_3P_2DES_KEY1, {0x1E8, 0x10}}, {WIRELESS_CONTROLLER_3P_2DES_KEY2, {0x1F8, 0x10}}, {WIRED_WEBCAM_3P_2DES_KEY1, {0x208, 0x10}}, {WIRED_WEBCAM_3P_2DES_KEY2, {0x218, 0x10}}, {WIRED_CONTROLLER_3P_2DES_KEY1, {0x228, 0x10}}, {WIRED_CONTROLLER_3P_2DES_KEY2, {0x238, 0x10}}, {MEMORY_UNIT_3P_2DES_KEY1, {0x248, 0x10}}, {MEMORY_UNIT_3P_2DES_KEY2, {0x258, 0x10}}, {OTHER_XSM3_DEVICE_3P_2DES_KEY1, {0x268, 0x10}}, {OTHER_XSM3_DEVICE_3P_2DES_KEY2, {0x278, 0x10}}, {CONSOLE_PRIVATE_KEY, {0x288, 0x1D0}}, {XEIKA_PRIVATE_KEY, {0x458, 0x390}}, {CARDEA_PRIVATE_KEY, {0x7E8, 0x1D0}}, {CONSOLE_CERTIFICATE, {0x9B8, 0x1A8}}, {XEIKA_CERTIFICATE, {0xB60, 0x1288}}, {SPECIAL_KEY_VAULT_SIGNATURE, {0x1DF8, 0x100}}, {CARDEA_CERTIFICATE, {0x1EE8, 0x2108}}, }; static const uint8_t xe_key_obfuscation_key[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; #pragma pack(push, 1) struct XE_CONSOLE_ID { union { struct { uint8_t RefurbBits : 4; uint8_t ManufactureMonth : 4; uint32_t ManufactureYear : 4; uint32_t MacIndex3 : 8; uint32_t MacIndex4 : 8; uint32_t MacIndex5 : 8; uint32_t Crc : 4; }; uint8_t Data[5]; }; }; #pragma pack(pop) struct X_CONSOLE_PUBLIC_KEY { uint8_t public_exponent[0x4]; // 0x0 sz:0x4 uint8_t modulus[0x80]; // 0x4 sz:0x80 }; // size 132 static_assert_size(X_CONSOLE_PUBLIC_KEY, 0x84); enum XConsoleType : uint32_t { Invalid = 0, Devkit = 1, Retail = 2, Testkit = 0x40000001, RecoveredDevkit = 0x80000001, Pre1888Devkit = 0x80000002 }; struct XE_CONSOLE_CERTIFICATE { xe::be cert_size; // 0x0 sz:0x2 XE_CONSOLE_ID console_id; // 0x2 sz:0x5 uint8_t console_part_number[11]; // 0x7 sz:0xB uint8_t reserved[4]; // 0x12 sz:0x4 xe::be privileges; // 0x16 sz:0x2 xe::be console_type; // 0x18 sz:0x4 uint8_t manufacture_date[8]; // 0x1C sz:0x8 X_CONSOLE_PUBLIC_KEY console_public_key; // 0x24 sz:0x84 XECRYPT_SIG signature; // 0xA8 sz:0x100 }; // size 424 static_assert_size(XE_CONSOLE_CERTIFICATE, 0x1A8); struct XE_CONSOLE_SIGNATURE { XE_CONSOLE_CERTIFICATE console_certificate; uint8_t signature[0x80]; }; static_assert_size(XE_CONSOLE_SIGNATURE, 0x228); void XeCryptRc4Key_entry(pointer_t rc4_ctx, lpvoid_t key, dword_t key_size) { // Setup RC4 state rc4_ctx->i = rc4_ctx->j = 0; for (uint32_t x = 0; x < 0x100; x++) { rc4_ctx->S[x] = (uint8_t)x; } uint32_t idx = 0; for (uint32_t x = 0; x < 0x100; x++) { idx = (idx + rc4_ctx->S[x] + key[x % 0x10]) % 0x100; uint8_t temp = rc4_ctx->S[idx]; rc4_ctx->S[idx] = rc4_ctx->S[x]; rc4_ctx->S[x] = temp; } } DECLARE_XBOXKRNL_EXPORT1(XeCryptRc4Key, kNone, kImplemented); void XeCryptRc4Ecb_entry(pointer_t rc4_ctx, lpvoid_t data, dword_t size) { // Crypt data for (uint32_t idx = 0; idx < size; idx++) { rc4_ctx->i = (rc4_ctx->i + 1) % 0x100; rc4_ctx->j = (rc4_ctx->j + rc4_ctx->S[rc4_ctx->i]) % 0x100; uint8_t temp = rc4_ctx->S[rc4_ctx->i]; rc4_ctx->S[rc4_ctx->i] = rc4_ctx->S[rc4_ctx->j]; rc4_ctx->S[rc4_ctx->j] = temp; uint8_t a = data[idx]; uint8_t b = rc4_ctx->S[(rc4_ctx->S[rc4_ctx->i] + rc4_ctx->S[rc4_ctx->j]) % 0x100]; data[idx] = (uint8_t)(a ^ b); } } DECLARE_XBOXKRNL_EXPORT1(XeCryptRc4Ecb, kNone, kImplemented); void XeCryptRc4_entry(lpvoid_t key, dword_t key_size, lpvoid_t data, dword_t size) { XECRYPT_RC4_STATE rc4_ctx; XeCryptRc4Key_entry(&rc4_ctx, key, key_size); XeCryptRc4Ecb_entry(&rc4_ctx, data, size); } DECLARE_XBOXKRNL_EXPORT1(XeCryptRc4, kNone, kImplemented); void InitSha1(sha1::SHA1* sha, const XECRYPT_SHA_STATE* state) { uint32_t digest[5]; std::copy(std::begin(state->state), std::end(state->state), digest); sha->init(digest, state->buffer, state->count); } void StoreSha1(const sha1::SHA1* sha, XECRYPT_SHA_STATE* state) { std::copy_n(sha->getDigest(), xe::countof(state->state), state->state); state->count = static_cast(sha->getByteCount()); std::copy_n(sha->getBlock(), sha->getBlockByteIndex(), state->buffer); } void XeCryptShaInit_entry(pointer_t sha_state) { sha_state.Zero(); sha_state->state[0] = 0x67452301; sha_state->state[1] = 0xEFCDAB89; sha_state->state[2] = 0x98BADCFE; sha_state->state[3] = 0x10325476; sha_state->state[4] = 0xC3D2E1F0; } DECLARE_XBOXKRNL_EXPORT1(XeCryptShaInit, kNone, kImplemented); void XeCryptShaUpdate_entry(pointer_t sha_state, lpvoid_t input, dword_t input_size) { sha1::SHA1 sha; InitSha1(&sha, sha_state); sha.processBytes(input, input_size); StoreSha1(&sha, sha_state); } DECLARE_XBOXKRNL_EXPORT1(XeCryptShaUpdate, kNone, kImplemented); void XeCryptShaFinal_entry(pointer_t sha_state, pointer_t out, dword_t out_size) { sha1::SHA1 sha; InitSha1(&sha, sha_state); uint8_t digest[0x14]; sha.finalize(digest); std::copy_n(digest, std::min(xe::countof(digest), out_size), static_cast(out)); std::copy_n(sha.getDigest(), xe::countof(sha_state->state), sha_state->state); } DECLARE_XBOXKRNL_EXPORT1(XeCryptShaFinal, kNone, kImplemented); void XeCryptSha_entry(lpvoid_t input_1, dword_t input_1_size, lpvoid_t input_2, dword_t input_2_size, lpvoid_t input_3, dword_t input_3_size, lpvoid_t output, dword_t output_size) { sha1::SHA1 sha; if (input_1 && input_1_size) { sha.processBytes(input_1, input_1_size); } if (input_2 && input_2_size) { sha.processBytes(input_2, input_2_size); } if (input_3 && input_3_size) { sha.processBytes(input_3, input_3_size); } uint8_t digest[0x14]; sha.finalize(digest); std::copy_n(digest, std::min(xe::countof(digest), output_size), output.as()); } DECLARE_XBOXKRNL_EXPORT1(XeCryptSha, kNone, kImplemented); void XeCryptMd5_entry(lpvoid_t input_1, dword_t input_1_size, lpvoid_t input_2, dword_t input_2_size, lpvoid_t input_3, dword_t input_3_size, lpvoid_t output, dword_t output_size) { AVMD5* md5 = av_md5_alloc(); av_md5_init(md5); if (input_1 && input_1_size) { av_md5_update(md5, input_1, input_1_size); } if (input_2 && input_2_size) { av_md5_update(md5, input_2, input_2_size); } if (input_3 && input_3_size) { av_md5_update(md5, input_3, input_3_size); } uint8_t digest[16]; av_md5_final(md5, digest); std::copy_n(digest, std::min(xe::countof(digest), output_size), output.as()); } DECLARE_XBOXKRNL_EXPORT1(XeCryptMd5, kNone, kImplemented); void XeCryptSha256Init_entry(pointer_t sha_state) { sha_state.Zero(); sha_state->state[0] = 0x6a09e667; sha_state->state[1] = 0xbb67ae85; sha_state->state[2] = 0x3c6ef372; sha_state->state[3] = 0xa54ff53a; sha_state->state[4] = 0x510e527f; sha_state->state[5] = 0x9b05688c; sha_state->state[6] = 0x1f83d9ab; sha_state->state[7] = 0x5be0cd19; } DECLARE_XBOXKRNL_EXPORT1(XeCryptSha256Init, kNone, kImplemented); void XeCryptSha256Update_entry(pointer_t sha_state, lpvoid_t input, dword_t input_size) { sha256::SHA256 sha; std::copy(std::begin(sha_state->state), std::end(sha_state->state), sha.getHashValues()); std::copy(std::begin(sha_state->buffer), std::end(sha_state->buffer), sha.getBuffer()); sha.setTotalSize(sha_state->count); sha.add(input, input_size); std::copy_n(sha.getHashValues(), xe::countof(sha_state->state), sha_state->state); std::copy_n(sha.getBuffer(), xe::countof(sha_state->buffer), sha_state->buffer); sha_state->count = static_cast(sha.getTotalSize()); } DECLARE_XBOXKRNL_EXPORT1(XeCryptSha256Update, kNone, kImplemented); void XeCryptSha256Final_entry(pointer_t sha_state, pointer_t out, dword_t out_size) { sha256::SHA256 sha; std::copy(std::begin(sha_state->state), std::end(sha_state->state), sha.getHashValues()); std::copy(std::begin(sha_state->buffer), std::end(sha_state->buffer), sha.getBuffer()); sha.setTotalSize(sha_state->count); uint8_t hash[32]; sha.getHash(hash); std::copy_n(hash, std::min(xe::countof(hash), out_size), static_cast(out)); std::copy(std::begin(hash), std::end(hash), sha_state->buffer); } DECLARE_XBOXKRNL_EXPORT1(XeCryptSha256Final, kNone, kImplemented); void XeCryptSha512Init_entry(pointer_t sha_state) { sha_state.Zero(); sha_state->state[0] = 0x6a09e667f3bcc908; sha_state->state[1] = 0xbb67ae8584caa73b; sha_state->state[2] = 0x3c6ef372fe94f82b; sha_state->state[3] = 0xa54ff53a5f1d36f1; sha_state->state[4] = 0x510e527fade682d1; sha_state->state[5] = 0x9b05688c2b3e6c1f; sha_state->state[6] = 0x1f83d9abfb41bd6b; sha_state->state[7] = 0x5be0cd19137e2179; } DECLARE_XBOXKRNL_EXPORT1(XeCryptSha512Init, kNone, kImplemented); void XeCryptSha512Update_entry(pointer_t sha_state, lpvoid_t input, dword_t input_size) { AVSHA512* sha = av_sha512_alloc(); av_sha512_init(sha, 512); // Trick to make similar implementation as SHA256 SHA512_STATE* sha2 = reinterpret_cast(sha); std::copy(std::begin(sha_state->state), std::end(sha_state->state), sha2->state); std::copy(std::begin(sha_state->buffer), std::end(sha_state->buffer), sha2->buffer); sha2->count = sha_state->count; // Add new entry from input av_sha512_update(sha, input, input_size); // Copy back data to guest sha_state std::copy_n(sha2->state, xe::countof(sha_state->state), sha_state->state); std::copy_n(sha2->buffer, xe::countof(sha_state->buffer), sha_state->buffer); sha_state->count = sha2->count; } DECLARE_XBOXKRNL_EXPORT1(XeCryptSha512Update, kNone, kImplemented); void XeCryptSha512Final_entry(pointer_t sha_state, pointer_t out, dword_t out_size) { AVSHA512* sha = av_sha512_alloc(); av_sha512_init(sha, 512); // Trick to make similar implementation as SHA256 SHA512_STATE* sha2 = reinterpret_cast(sha); std::copy(std::begin(sha_state->state), std::end(sha_state->state), sha2->state); std::copy(std::begin(sha_state->buffer), std::end(sha_state->buffer), sha2->buffer); sha2->count = sha_state->count; uint8_t hash[64]; av_sha512_final(sha, hash); std::copy_n(hash, std::min(xe::countof(hash), out_size), static_cast(out)); std::copy(std::begin(hash), std::end(hash), sha_state->buffer); } DECLARE_XBOXKRNL_EXPORT1(XeCryptSha512Final, kNone, kImplemented); void XeCryptMd5Init_entry(pointer_t md5_state) { md5_state.Zero(); // must match av_md5_init md5_state->state[0] = 0x10325476; md5_state->state[1] = 0x98badcfe; md5_state->state[2] = 0xefcdab89; md5_state->state[3] = 0x67452301; } DECLARE_XBOXKRNL_EXPORT1(XeCryptMd5Init, kNone, kImplemented); void XeCryptMd5Update_entry(pointer_t md5_state, lpvoid_t input, dword_t input_size) { AVMD5* md5 = av_md5_alloc(); av_md5_init(md5); // Trick to make similar implementation as SHA256 MD5_STATE* md5InternalState = reinterpret_cast(md5); std::copy(std::begin(md5_state->state), std::end(md5_state->state), md5InternalState->ABCD); std::copy(std::begin(md5_state->buffer), std::end(md5_state->buffer), md5InternalState->block); md5InternalState->len = md5_state->count; // Add new entry from input av_md5_update(md5, input, input_size); // Copy back data to guest md5_state std::copy_n(md5InternalState->ABCD, xe::countof(md5_state->state), md5_state->state); std::copy_n(md5InternalState->block, xe::countof(md5_state->buffer), md5_state->buffer); md5_state->count = md5InternalState->len; } DECLARE_XBOXKRNL_EXPORT1(XeCryptMd5Update, kNone, kImplemented); void XeCryptMd5Final_entry(pointer_t md5_state, pointer_t out, dword_t out_size) { AVMD5* md5 = av_md5_alloc(); av_md5_init(md5); // Trick to make similar implementation as SHA256 MD5_STATE* md5InternalState = reinterpret_cast(md5); std::copy(std::begin(md5_state->state), std::end(md5_state->state), md5InternalState->ABCD); std::copy(std::begin(md5_state->buffer), std::end(md5_state->buffer), md5InternalState->block); md5InternalState->len = md5_state->count; uint8_t hash[16]; av_md5_final(md5, hash); std::copy_n(hash, std::min(xe::countof(hash), out_size), static_cast(out)); std::copy(std::begin(hash), std::end(hash), md5_state->buffer); } DECLARE_XBOXKRNL_EXPORT1(XeCryptMd5Final, kNone, kImplemented); // Byteswaps each 8 bytes void XeCryptBnQw_SwapDwQwLeBe_entry(pointer_t qw_inp, pointer_t qw_out, dword_t size) { xe::copy_and_swap(qw_out, qw_inp, size); } DECLARE_XBOXKRNL_EXPORT1(XeCryptBnQw_SwapDwQwLeBe, kNone, kImplemented); dword_result_t XeCryptBnQwNeRsaPubCrypt_entry(pointer_t qw_a, pointer_t qw_b, pointer_t rsa) { uint32_t num_qwords = rsa->size; uint32_t exponent = rsa->public_exponent; const uint8_t* input_bytes = reinterpret_cast(&qw_a[0]); uint8_t* output_bytes = reinterpret_cast(&qw_b[0]); const uint8_t* mod_bytes = reinterpret_cast(&rsa[1]); // modulus follows header return XeCryptBnQwNeRsaPubCrypt(input_bytes, output_bytes, mod_bytes, num_qwords, exponent); } DECLARE_XBOXKRNL_EXPORT1(XeCryptBnQwNeRsaPubCrypt, kNone, kImplemented); dword_result_t XeCryptBnQwBeSigVerify_entry(pointer_t sig, lpvoid_t hash, lpstring_t salt, pointer_t rsa) { // hash from XeCryptRotSumSha // known salts are "XBOX360SVC", "XBOX360XTT", "TDBXBOX36O" return true; } DECLARE_XBOXKRNL_EXPORT1(XeCryptBnQwBeSigVerify, kNone, kStub); dword_result_t XeCryptBnDwLePkcs1Verify_entry(lpvoid_t hash, lpvoid_t sig, dword_t size) { // BOOL return value return 1; } DECLARE_XBOXKRNL_EXPORT1(XeCryptBnDwLePkcs1Verify, kNone, kStub); void XeCryptRandom_entry(lpvoid_t buf, dword_t buf_size) { std::memset(buf, 0xFD, buf_size); } DECLARE_XBOXKRNL_EXPORT1(XeCryptRandom, kNone, kStub); void XeCryptDesKey_entry(pointer_t state_ptr, lpqword_t key) { DES des(key[0]); std::memcpy(state_ptr->keytab, des.get_sub_key(), 128); } DECLARE_XBOXKRNL_EXPORT1(XeCryptDesKey, kNone, kImplemented); void XeCryptDesEcb_entry(pointer_t state_ptr, lpqword_t inp, lpqword_t out, dword_t encrypt) { DES des(reinterpret_cast(state_ptr->keytab)); if (encrypt) { *out = des.encrypt(*inp); } else { *out = des.decrypt(*inp); } } DECLARE_XBOXKRNL_EXPORT1(XeCryptDesEcb, kNone, kImplemented); // Sets bit 0 to make the parity odd void XeCryptDesParity_entry(lpvoid_t inp, dword_t inp_size, lpvoid_t out_ptr) { DES::set_parity(inp, inp_size, out_ptr); } DECLARE_XBOXKRNL_EXPORT1(XeCryptDesParity, kNone, kImplemented); void XeCryptDes3Key_entry(pointer_t state_ptr, lpqword_t key) { DES3 des3(key[0], key[1], key[2]); DES* des = des3.getDES(); // Store our DES state into the state. for (int i = 0; i < 3; i++) { std::memcpy(state_ptr->des_state[i].keytab, des[i].get_sub_key(), 128); } } DECLARE_XBOXKRNL_EXPORT1(XeCryptDes3Key, kNone, kImplemented); void XeCryptDes3Ecb_entry(pointer_t state_ptr, lpqword_t inp, lpqword_t out, dword_t encrypt) { DES3 des3((ui64*)state_ptr->des_state[0].keytab, (ui64*)state_ptr->des_state[1].keytab, (ui64*)state_ptr->des_state[2].keytab); if (encrypt) { *out = des3.encrypt(*inp); } else { *out = des3.decrypt(*inp); } } DECLARE_XBOXKRNL_EXPORT1(XeCryptDes3Ecb, kNone, kImplemented); void XeCryptDes3Cbc_entry(pointer_t state_ptr, lpqword_t inp, dword_t inp_size, lpqword_t out, lpqword_t feed, dword_t encrypt) { DES3 des3((ui64*)state_ptr->des_state[0].keytab, (ui64*)state_ptr->des_state[1].keytab, (ui64*)state_ptr->des_state[2].keytab); // DES can only do 8-byte chunks at a time! assert_true(inp_size % 8 == 0); uint64_t last_block = *feed; for (uint32_t i = 0; i < inp_size / 8; i++) { uint64_t block = inp[i]; if (encrypt) { last_block = des3.encrypt(block ^ last_block); out[i] = last_block; } else { out[i] = des3.decrypt(block) ^ last_block; last_block = block; } } *feed = last_block; } DECLARE_XBOXKRNL_EXPORT1(XeCryptDes3Cbc, kNone, kImplemented); static inline uint8_t xeXeCryptAesMul2(uint8_t a) { return (a & 0x80) ? ((a << 1) ^ 0x1B) : (a << 1); } void XeCryptAesKey_entry(pointer_t state_ptr, lpvoid_t key) { aes_key_schedule_128(key, reinterpret_cast(state_ptr->keytabenc)); // Decryption key schedule not needed by openluopworld/aes_128, but generated // to fill the context structure properly. std::memcpy(state_ptr->keytabdec[0], state_ptr->keytabenc[10], 16); // Inverse MixColumns. for (uint32_t i = 1; i < 10; ++i) { const uint8_t* enc = reinterpret_cast(state_ptr->keytabenc[10 - i]); uint8_t* dec = reinterpret_cast(state_ptr->keytabdec[i]); uint8_t t, u, v; t = enc[0] ^ enc[1] ^ enc[2] ^ enc[3]; dec[0] = t ^ enc[0] ^ xeXeCryptAesMul2(enc[0] ^ enc[1]); dec[1] = t ^ enc[1] ^ xeXeCryptAesMul2(enc[1] ^ enc[2]); dec[2] = t ^ enc[2] ^ xeXeCryptAesMul2(enc[2] ^ enc[3]); dec[3] = t ^ enc[3] ^ xeXeCryptAesMul2(enc[3] ^ enc[0]); u = xeXeCryptAesMul2(xeXeCryptAesMul2(enc[0] ^ enc[2])); v = xeXeCryptAesMul2(xeXeCryptAesMul2(enc[1] ^ enc[3])); t = xeXeCryptAesMul2(u ^ v); dec[0] ^= t ^ u; dec[1] ^= t ^ v; dec[2] ^= t ^ u; dec[3] ^= t ^ v; t = enc[4] ^ enc[5] ^ enc[6] ^ enc[7]; dec[4] = t ^ enc[4] ^ xeXeCryptAesMul2(enc[4] ^ enc[5]); dec[5] = t ^ enc[5] ^ xeXeCryptAesMul2(enc[5] ^ enc[6]); dec[6] = t ^ enc[6] ^ xeXeCryptAesMul2(enc[6] ^ enc[7]); dec[7] = t ^ enc[7] ^ xeXeCryptAesMul2(enc[7] ^ enc[4]); u = xeXeCryptAesMul2(xeXeCryptAesMul2(enc[4] ^ enc[6])); v = xeXeCryptAesMul2(xeXeCryptAesMul2(enc[5] ^ enc[7])); t = xeXeCryptAesMul2(u ^ v); dec[4] ^= t ^ u; dec[5] ^= t ^ v; dec[6] ^= t ^ u; dec[7] ^= t ^ v; t = enc[8] ^ enc[9] ^ enc[10] ^ enc[11]; dec[8] = t ^ enc[8] ^ xeXeCryptAesMul2(enc[8] ^ enc[9]); dec[9] = t ^ enc[9] ^ xeXeCryptAesMul2(enc[9] ^ enc[10]); dec[10] = t ^ enc[10] ^ xeXeCryptAesMul2(enc[10] ^ enc[11]); dec[11] = t ^ enc[11] ^ xeXeCryptAesMul2(enc[11] ^ enc[8]); u = xeXeCryptAesMul2(xeXeCryptAesMul2(enc[8] ^ enc[10])); v = xeXeCryptAesMul2(xeXeCryptAesMul2(enc[9] ^ enc[11])); t = xeXeCryptAesMul2(u ^ v); dec[8] ^= t ^ u; dec[9] ^= t ^ v; dec[10] ^= t ^ u; dec[11] ^= t ^ v; t = enc[12] ^ enc[13] ^ enc[14] ^ enc[15]; dec[12] = t ^ enc[12] ^ xeXeCryptAesMul2(enc[12] ^ enc[13]); dec[13] = t ^ enc[13] ^ xeXeCryptAesMul2(enc[13] ^ enc[14]); dec[14] = t ^ enc[14] ^ xeXeCryptAesMul2(enc[14] ^ enc[15]); dec[15] = t ^ enc[15] ^ xeXeCryptAesMul2(enc[15] ^ enc[12]); u = xeXeCryptAesMul2(xeXeCryptAesMul2(enc[12] ^ enc[14])); v = xeXeCryptAesMul2(xeXeCryptAesMul2(enc[13] ^ enc[15])); t = xeXeCryptAesMul2(u ^ v); dec[12] ^= t ^ u; dec[13] ^= t ^ v; dec[14] ^= t ^ u; dec[15] ^= t ^ v; } std::memcpy(state_ptr->keytabdec[10], state_ptr->keytabenc[0], 16); // TODO(Triang3l): Verify the order in keytabenc and everything in keytabdec. } DECLARE_XBOXKRNL_EXPORT1(XeCryptAesKey, kNone, kImplemented); void XeCryptAesEcb_entry(pointer_t state_ptr, lpvoid_t inp_ptr, lpvoid_t out_ptr, dword_t encrypt) { const uint8_t* keytab = reinterpret_cast(state_ptr->keytabenc); if (encrypt) { aes_encrypt_128(keytab, inp_ptr, out_ptr); } else { aes_decrypt_128(keytab, inp_ptr, out_ptr); } } DECLARE_XBOXKRNL_EXPORT1(XeCryptAesEcb, kNone, kImplemented); void XeCryptAesCbc_entry(pointer_t state_ptr, lpvoid_t inp_ptr, dword_t inp_size, lpvoid_t out_ptr, lpvoid_t feed_ptr, dword_t encrypt) { const uint8_t* keytab = reinterpret_cast(state_ptr->keytabenc); const uint8_t* inp = inp_ptr.as(); uint8_t* out = out_ptr.as(); uint8_t* feed = feed_ptr.as(); if (encrypt) { for (uint32_t i = 0; i < inp_size; i += 16) { for (uint32_t j = 0; j < 16; ++j) { feed[j] ^= inp[j]; } aes_encrypt_128(keytab, feed, feed); std::memcpy(out, feed, 16); inp += 16; out += 16; } } else { for (uint32_t i = 0; i < inp_size; i += 16) { // In case inp == out. uint8_t tmp[16]; std::memcpy(tmp, inp, 16); aes_decrypt_128(keytab, inp, out); for (uint32_t j = 0; j < 16; ++j) { out[j] ^= feed[j]; } std::memcpy(feed, tmp, 16); inp += 16; out += 16; } } } DECLARE_XBOXKRNL_EXPORT1(XeCryptAesCbc, kNone, kImplemented); void XeCryptHmacSha_entry(lpvoid_t key, dword_t key_size_in, lpvoid_t inp_1, dword_t inp_1_size, lpvoid_t inp_2, dword_t inp_2_size, lpvoid_t inp_3, dword_t inp_3_size, lpvoid_t out, dword_t out_size) { uint32_t key_size = key_size_in; sha1::SHA1 sha; uint8_t kpad_i[0x40]; uint8_t kpad_o[0x40]; uint8_t tmp_key[0x40]; std::memset(kpad_i, 0x36, 0x40); std::memset(kpad_o, 0x5C, 0x40); // Setup HMAC key // If > block size, use its hash if (key_size > 0x40) { sha1::SHA1 sha_key; sha_key.processBytes(key, key_size); sha_key.finalize((uint8_t*)tmp_key); key_size = 0x14u; } else { std::memcpy(tmp_key, key, key_size); } for (uint32_t i = 0; i < key_size; i++) { kpad_i[i] = tmp_key[i] ^ 0x36; kpad_o[i] = tmp_key[i] ^ 0x5C; } // Inner sha.processBytes(kpad_i, 0x40); if (inp_1_size) { sha.processBytes(inp_1, inp_1_size); } if (inp_2_size) { sha.processBytes(inp_2, inp_2_size); } if (inp_3_size) { sha.processBytes(inp_3, inp_3_size); } uint8_t digest[0x14]; sha.finalize(digest); sha.reset(); // Outer sha.processBytes(kpad_o, 0x40); sha.processBytes(digest, 0x14); sha.finalize(digest); std::memcpy(out, digest, std::min((uint32_t)out_size, 0x14u)); } DECLARE_XBOXKRNL_EXPORT1(XeCryptHmacSha, kNone, kImplemented); dword_result_t XeKeysGetKeyProperties_entry(dword_t key) { if (X_Key_Properties.contains(key)) { auto& key_info = X_Key_Properties.at(key); return std::get<1>(key_info); } XELOGW("Key 0x{:04X} not implemented", static_cast(key)); return 0; } DECLARE_XBOXKRNL_EXPORT1(XeKeysGetKeyProperties, kNone, kImplemented); dword_result_t XeKeysGetKey_entry(word_t key, lpvoid_t key_buffer, lpdword_t key_length) { if (key_vault.contains(key)) { std::span key_needed = key_vault.at(key); std::memcpy(key_buffer, key_needed.data(), *key_length); } else { XELOGW("Key 0x{:04X} not implemented", static_cast(key)); } return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XeKeysGetKey, kNone, kSketchy); dword_result_t XeKeysHmacSha_entry(dword_t key_num, lpvoid_t inp_1, dword_t inp_1_size, lpvoid_t inp_2, dword_t inp_2_size, lpvoid_t inp_3, dword_t inp_3_size, lpvoid_t out, dword_t out_size) { const uint8_t* key = nullptr; if (key_num == 0x19) { key = key19; } if (key) { XeCryptHmacSha_entry((void*)key, 0x10, inp_1, inp_1_size, inp_2, inp_2_size, inp_3, inp_3_size, out, out_size); return X_STATUS_SUCCESS; } return X_STATUS_UNSUCCESSFUL; } DECLARE_XBOXKRNL_EXPORT1(XeKeysHmacSha, kNone, kImplemented); void XeCryptRotSumSha_entry(lpvoid_t inp_1, dword_t inp_1_size, lpvoid_t inp_2, dword_t inp_2_size, lpvoid_t out, dword_t out_size) { // out used by XeCryptBnQwBeSigVerify } DECLARE_XBOXKRNL_EXPORT1(XeCryptRotSumSha, kNone, kStub); dword_result_t XeKeysAesCbcUsingKey_entry(lpvoid_t obscured_key, lpvoid_t inp_ptr, dword_t inp_size, lpvoid_t out_ptr, lpvoid_t feed_ptr, dword_t encrypt) { uint8_t key[16]; // Deobscure key XECRYPT_AES_STATE aes; XeCryptAesKey_entry(&aes, (uint8_t*)xe_key_obfuscation_key); XeCryptAesEcb_entry(&aes, obscured_key, key, 0); // Run CBC using deobscured key XeCryptAesKey_entry(&aes, key); XeCryptAesCbc_entry(&aes, inp_ptr, inp_size, out_ptr, feed_ptr, encrypt); return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XeKeysAesCbcUsingKey, kNone, kImplemented); dword_result_t XeKeysObscureKey_entry(lpvoid_t input, lpvoid_t output) { // Based on HvxKeysObscureKey // Seems to encrypt input with per-console KEY_OBFUSCATION_KEY (key 0x18) XECRYPT_AES_STATE aes; XeCryptAesKey_entry(&aes, (uint8_t*)xe_key_obfuscation_key); XeCryptAesEcb_entry(&aes, input, output, 1); return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XeKeysObscureKey, kNone, kImplemented); dword_result_t XeKeysHmacShaUsingKey_entry(lpvoid_t obscured_key, lpvoid_t inp_1, dword_t inp_1_size, lpvoid_t inp_2, dword_t inp_2_size, lpvoid_t inp_3, dword_t inp_3_size, lpvoid_t out, dword_t out_size) { if (!obscured_key) { return X_STATUS_INVALID_PARAMETER; } uint8_t key[16]; // Deobscure key XECRYPT_AES_STATE aes; XeCryptAesKey_entry(&aes, (uint8_t*)xe_key_obfuscation_key); XeCryptAesEcb_entry(&aes, obscured_key, key, 0); XeCryptHmacSha_entry(key, 0x10, inp_1, inp_1_size, inp_2, inp_2_size, inp_3, inp_3_size, out, out_size); return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XeKeysHmacShaUsingKey, kNone, kImplemented); dword_result_t XeKeysGetConsoleType_entry(lpdword_t type_out) { *type_out = Retail; return 0; } DECLARE_XBOXKRNL_EXPORT1(XeKeysGetConsoleType, kNone, kImplemented); dword_result_t XeKeysGetConsoleID_entry(pointer_t raw_bytes, lpstring_t hex_string) { // We dont care about KV or using official keys if (raw_bytes) { raw_bytes.Zero(); raw_bytes->RefurbBits = 0b0011; raw_bytes->ManufactureMonth = 0b1001; raw_bytes->ManufactureYear = 0b0001; raw_bytes->MacIndex3 = 0b01000000; raw_bytes->MacIndex4 = 0b01100110; raw_bytes->MacIndex5 = 0b01111110; raw_bytes->Crc = 0b0000; } if (hex_string) { std::string key = "245149100000"; std::memcpy(hex_string, key.c_str(), 0xC); } return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XeKeysGetConsoleID, kNone, kImplemented); dword_result_t XeKeysConsolePrivateKeySign_entry( lpvoid_t hash, pointer_t output) { if (!hash) { return false; } if (!output) { return false; } output.Zero(); output->console_id.RefurbBits = 0b0011; output->console_id.ManufactureMonth = 0b1001; output->console_id.ManufactureYear = 0b0001; output->console_id.MacIndex3 = 0b01000000; output->console_id.MacIndex4 = 0b01100110; output->console_id.MacIndex5 = 0b01111110; output->console_id.Crc = 0b0000; output->console_type = Retail; output->manufacture_date[0] = 2; output->manufacture_date[1] = 0; output->manufacture_date[2] = 0; output->manufacture_date[3] = 5; output->manufacture_date[4] = 1; output->manufacture_date[5] = 1; output->manufacture_date[6] = 2; output->manufacture_date[7] = 2; return true; } DECLARE_XBOXKRNL_EXPORT1(XeKeysConsolePrivateKeySign, kNone, kSketchy); } // namespace xboxkrnl } // namespace kernel } // namespace xe DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Crypt);