From 176119817341280ef4e4cca6d6b18b59c822ccf2 Mon Sep 17 00:00:00 2001 From: hayleyxyz <7829319+hayleyxyz@users.noreply.github.com.> Date: Sun, 9 Aug 2026 17:10:27 +0100 Subject: [PATCH 1/2] Add CMakeLists.txt --- CMakeLists.txt | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f51da7d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,41 @@ +add_library(ExCrypt STATIC + src/excrypt.h + src/excrypt_aes.c + src/excrypt_aes.h + src/excrypt_bn.c + src/excrypt_bn.h + src/excrypt_bn_key.cpp + src/excrypt_bn_mod.cpp + src/excrypt_bn_pkcs1.cpp + src/excrypt_bn_rsa.cpp + src/excrypt_bn_sig.c + src/excrypt_des.c + src/excrypt_des.h + src/excrypt_des_data.h + src/excrypt_ecc.c + src/excrypt_ecc.h + src/excrypt_md5.c + src/excrypt_md5.h + src/excrypt_mem.cpp + src/excrypt_mem.h + src/excrypt_parve.c + src/excrypt_parve.h + src/excrypt_rc4.c + src/excrypt_rc4.h + src/excrypt_rotsum.c + src/excrypt_rotsum.h + src/excrypt_sha.c + src/excrypt_sha.h + src/excrypt_sha2.c + src/excrypt_sha2.h + src/exkeys.cpp + src/exkeys.h + src/fp61.hpp + src/rijndael.c + src/rijndael.h) + +target_include_directories(ExCrypt PUBLIC src) + +if(WIN32) + target_link_libraries(ExCrypt PUBLIC bcrypt) +endif() From eca96acbb0b145b19a48e7ea8f7dfdceda33b28a Mon Sep 17 00:00:00 2001 From: hayleyxyz <7829319+hayleyxyz@users.noreply.github.com.> Date: Wed, 12 Aug 2026 21:10:45 +0100 Subject: [PATCH 2/2] Update CMake build to be portable, make code compile on MSYS, add limited tests for AES/RC4/SHA1 --- CMakeLists.txt | 19 +++++++++++++ README.md | 15 +++++++++-- src/excrypt.h | 2 +- src/excrypt_bn_pkcs1.cpp | 2 +- src/excrypt_bn_rsa.cpp | 2 ++ src/excrypt_sha2.c | 4 +-- src/exkeys.cpp | 7 +++-- src/portable_io.c | 16 +++++++++++ src/portable_io.h | 8 ++++++ tests/CMakeLists.txt | 13 +++++++++ tests/common.c | 32 ++++++++++++++++++++++ tests/test_aes.c | 58 ++++++++++++++++++++++++++++++++++++++++ tests/test_rc4.c | 32 ++++++++++++++++++++++ tests/test_sha.c | 29 ++++++++++++++++++++ tests/tests.c | 14 ++++++++++ tests/tests.h | 9 +++++++ 16 files changed, 254 insertions(+), 8 deletions(-) create mode 100644 src/portable_io.c create mode 100644 src/portable_io.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/common.c create mode 100644 tests/test_aes.c create mode 100644 tests/test_rc4.c create mode 100644 tests/test_sha.c create mode 100644 tests/tests.c create mode 100644 tests/tests.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f51da7d..3cc2fbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,9 @@ +cmake_minimum_required(VERSION 3.16) + +project(ExCrypt LANGUAGES C CXX) + +include(CTest) + add_library(ExCrypt STATIC src/excrypt.h src/excrypt_aes.c @@ -36,6 +42,19 @@ add_library(ExCrypt STATIC target_include_directories(ExCrypt PUBLIC src) +target_compile_features(ExCrypt PUBLIC c_std_11 cxx_std_17) + +set_target_properties(ExCrypt PROPERTIES POSITION_INDEPENDENT_CODE ON) + +# Enable AES-NI and SSE4.1 flags for excrypt_aes.c +if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang" AND (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|i[3-6]86")) + set_source_files_properties(src/excrypt_aes.c PROPERTIES COMPILE_OPTIONS "-maes;-msse4.1") +endif() + if(WIN32) target_link_libraries(ExCrypt PUBLIC bcrypt) endif() + +if(BUILD_TESTING) + add_subdirectory(tests) +endif() diff --git a/README.md b/README.md index 29e7aae..d766230 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ It's been designed to be as closely compatible to those functions as possible - As such, it should only be used where compatibility with existing Xbox 360 code/data is required. -### Contents +## Contents The goal of ExCrypt is to implement XeCrypt functions known to be used on the Xbox 360. @@ -14,7 +14,7 @@ We only target XeCrypt functions that are exported from the kernel, used during For a list of XeCrypt targets, their status, and any implementation-specific notes, take a look at the [implementation status page](https://github.com/emoose/ExCrypt/issues/5). -### Implementation +## Implementation Code has to make sure to set the exact same state variables & return values as the 360 functions do. @@ -27,3 +27,14 @@ Some effort to make the code a bit more readable, rather than just a straight as We only target x86 & x64 - maybe other platforms in the future, but for now there's not much use in supporting anything else. Where possible, making use of existing, well-known crypto code is always preferrable to needing to write your own (unless some XeCrypt oddity somehow prevents it, that is) + +## Building + +ExCrypt.sln is a Visual Studio 2019 solution that can be upgraded to later versions of Visual Studio if desired. + +Building via [CMake](https://cmake.org/download/) is the recommended method. After installing CMake, you can build ExCrypt with the following commands run from the root of the repository: + +```bash +cmake -S . -B build +cmake --build build +``` diff --git a/src/excrypt.h b/src/excrypt.h index 557973e..a79f205 100644 --- a/src/excrypt.h +++ b/src/excrypt.h @@ -15,7 +15,7 @@ extern "C" { #define ROTL64(data, bits) (((data) << (bits)) | ((data) >> (64 - (bits)))) -#ifndef _MSC_VER +#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__MINGW64__) #define _byteswap_ulong __builtin_bswap32 #define _byteswap_uint64 __builtin_bswap64 #endif diff --git a/src/excrypt_bn_pkcs1.cpp b/src/excrypt_bn_pkcs1.cpp index d684513..cf5ce5f 100644 --- a/src/excrypt_bn_pkcs1.cpp +++ b/src/excrypt_bn_pkcs1.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/src/excrypt_bn_rsa.cpp b/src/excrypt_bn_rsa.cpp index 9b6d55b..23632a1 100644 --- a/src/excrypt_bn_rsa.cpp +++ b/src/excrypt_bn_rsa.cpp @@ -1,5 +1,7 @@ #include "excrypt.h" +#include + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include diff --git a/src/excrypt_sha2.c b/src/excrypt_sha2.c index 10f84d6..8b564cd 100644 --- a/src/excrypt_sha2.c +++ b/src/excrypt_sha2.c @@ -391,7 +391,7 @@ void ExCryptSha256Final(EXCRYPT_SHA256_STATE* state, uint8_t* output, uint32_t o /* extract the hash value as bytes in case the hash buffer is */ /* misaligned for 32-bit words */ - for (i = 0; i < min(output_size, 0x20); ++i) + for (i = 0; i < (output_size < 0x20 ? output_size : 0x20); ++i) output[i] = ((state->hash[i >> 2] >> (8 * (~i & 3))) & 0xff); } @@ -693,7 +693,7 @@ void ExCryptSha512Final(EXCRYPT_SHA512_STATE* state, uint8_t* output, uint32_t o /* extract the hash value as bytes in case the hash buffer is */ /* misaligned for 32-bit words */ - for (i = 0; i < min(output_size, 0x40); ++i) + for (i = 0; i < (output_size < 0x40 ? output_size : 0x40); ++i) output[i] = ((state->hash[i >> 3] >> (8 * (~i & 7))) & 0xff); } diff --git a/src/exkeys.cpp b/src/exkeys.cpp index 88be790..bcbb20d 100644 --- a/src/exkeys.cpp +++ b/src/exkeys.cpp @@ -1,7 +1,10 @@ +#include #include #include #include - +#include +#include +#include "portable_io.h" #include "excrypt.h" std::map> kExKeyProperties = { @@ -246,7 +249,7 @@ uint32_t ExKeysGetConsoleID(uint8_t* raw_bytes, char* hex_string) uint64_t counter = 0; for (int i = 0; i < 5; i++) counter = console_cert[2 + i] + counter * 0x100; - sprintf_s(string, 0x10, "%011llu%llx", counter >> 4, counter & 0xF); + snprintf(string, 0x10, "%011llu%llx", counter >> 4, counter & 0xF); memcpy(hex_string, string, 0xC); } return 0; diff --git a/src/portable_io.c b/src/portable_io.c new file mode 100644 index 0000000..1f2947d --- /dev/null +++ b/src/portable_io.c @@ -0,0 +1,16 @@ +#include "portable_io.h" +#include + +#ifndef fopen_s +int fopen_s(FILE** file_p, const char* filename, const char* mode) +{ + if (!file_p || !filename || !mode) + return -1; + + *file_p = fopen(filename, mode); + if (!*file_p) + return errno; + + return 0; +} +#endif \ No newline at end of file diff --git a/src/portable_io.h b/src/portable_io.h new file mode 100644 index 0000000..2f3acf4 --- /dev/null +++ b/src/portable_io.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +#ifndef fopen_s +int fopen_s(FILE** pFile, const char* filename, const char* mode); +#endif \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..250340d --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.16) + + +add_executable(ExCryptTests + common.c + tests.c + test_aes.c + test_sha.c + test_rc4.c + tests.h) + +target_link_libraries(ExCryptTests PRIVATE ExCrypt) +add_test(NAME ExCryptTests COMMAND ExCryptTests) \ No newline at end of file diff --git a/tests/common.c b/tests/common.c new file mode 100644 index 0000000..3443f9f --- /dev/null +++ b/tests/common.c @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "tests.h" + +int expect_bytes(const char* name, const uint8_t* actual, const uint8_t* expected, size_t size) +{ + if (memcmp(actual, expected, size) == 0) + { + return 0; + } + + fprintf(stderr, "%s failed\n", name); + fprintf(stderr, " actual: "); + + for (size_t index = 0; index < size; index++) + { + fprintf(stderr, "%02x", actual[index]); + } + + fprintf(stderr, "\n expected: "); + + for (size_t index = 0; index < size; index++) + { + fprintf(stderr, "%02x", expected[index]); + } + + fprintf(stderr, "\n"); + + return 1; +} \ No newline at end of file diff --git a/tests/test_aes.c b/tests/test_aes.c new file mode 100644 index 0000000..ca92b7c --- /dev/null +++ b/tests/test_aes.c @@ -0,0 +1,58 @@ +#include +#include +#include + +#include "tests.h" +#include "excrypt.h" + +int test_aes(void) +{ + static const uint8_t key[16] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + }; + + static const uint8_t input[64] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 + }; + + static const uint8_t ecb_expected[16] = { + 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97 + }; + + static const uint8_t iv[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + + static const uint8_t cbc_expected[64] = { + 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, + 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, + 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, + 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 + }; + + EXCRYPT_AES_STATE state; + uint8_t output[64]; + uint8_t feed[16]; + int failures = 0; + + ExCryptAesKey(&state, key); + ExCryptAesEcb(&state, input, output, 1); + failures += expect_bytes("AES ECB encryption", output, ecb_expected, 16); + + ExCryptAesEcb(&state, output, output, 0); + failures += expect_bytes("AES ECB decryption", output, input, 16); + + memcpy(feed, iv, sizeof(feed)); + ExCryptAesCbc(&state, input, sizeof(input), output, feed, 1); + failures += expect_bytes("AES CBC encryption", output, cbc_expected, sizeof(output)); + + memcpy(feed, iv, sizeof(feed)); + ExCryptAesCbc(&state, output, sizeof(output), output, feed, 0); + failures += expect_bytes("AES CBC decryption", output, input, sizeof(input)); + + return failures; +} diff --git a/tests/test_rc4.c b/tests/test_rc4.c new file mode 100644 index 0000000..c8b057c --- /dev/null +++ b/tests/test_rc4.c @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "tests.h" +#include "excrypt.h" + +int test_rc4(void) +{ + static const uint8_t key[] = "Key"; + static const uint8_t plaintext[] = "Plaintext"; + static const uint8_t expected[] = { 0xbb, 0xf3, 0x16, 0xe8, 0xd9, 0x40, 0xaf, 0x0a, 0xd3 }; + + uint8_t output[sizeof(plaintext) - 1]; + + EXCRYPT_RC4_STATE state; + int failures = 0; + + memcpy(output, plaintext, sizeof(output)); + ExCryptRc4(key, sizeof(key) - 1, output, sizeof(output)); + + failures += expect_bytes("RC4 one-shot", output, expected, sizeof(output)); + + memcpy(output, plaintext, sizeof(output)); + ExCryptRc4Key(&state, key, sizeof(key) - 1); + ExCryptRc4Ecb(&state, output, 4); + ExCryptRc4Ecb(&state, output + 4, sizeof(output) - 4); + + failures += expect_bytes("RC4 streaming", output, expected, sizeof(output)); + + return failures; +} \ No newline at end of file diff --git a/tests/test_sha.c b/tests/test_sha.c new file mode 100644 index 0000000..7b79aa3 --- /dev/null +++ b/tests/test_sha.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "tests.h" +#include "excrypt.h" + +int test_sha(void) +{ + static const uint8_t expected[20] = { + 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, 0x25, + 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d + }; + + EXCRYPT_SHA_STATE state; + uint8_t output[20]; + int failures; + + ExCryptSha((const uint8_t*)"a", 1, (const uint8_t*)"b", 1, (const uint8_t*)"c", 1, output, sizeof(output)); + failures = expect_bytes("SHA-1 one-shot", output, expected, sizeof(output)); + + ExCryptShaInit(&state); + ExCryptShaUpdate(&state, (const uint8_t*)"a", 1); + ExCryptShaUpdate(&state, (const uint8_t*)"bc", 2); + ExCryptShaFinal(&state, output, sizeof(output)); + + failures += expect_bytes("SHA-1 streaming", output, expected, sizeof(output)); + return failures; +} diff --git a/tests/tests.c b/tests/tests.c new file mode 100644 index 0000000..f6f1f4e --- /dev/null +++ b/tests/tests.c @@ -0,0 +1,14 @@ +#include "tests.h" +#include + +int main(void) +{ + int failures = test_aes() + test_sha() + test_rc4(); + if (failures != 0) + { + return 1; + } + + printf("AES, SHA-1, and RC4 tests passed\n"); + return 0; +} \ No newline at end of file diff --git a/tests/tests.h b/tests/tests.h new file mode 100644 index 0000000..29a16e4 --- /dev/null +++ b/tests/tests.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +int expect_bytes(const char* name, const uint8_t* actual, const uint8_t* expected, size_t size); +int test_aes(void); +int test_sha(void); +int test_rc4(void); \ No newline at end of file