├── LICENSE ├── .gitmodules ├── include ├── cmake_vars_eap.h.in ├── cmake_vars_emc.h.in ├── cmake_vars_eapk.h.in ├── cmake_vars_syscon.h.in ├── syscon.h ├── patch.h ├── common.h ├── emc.h ├── key_store.h ├── crypto.h └── eap.h ├── .vscode ├── c_cpp_properties.json └── tasks.json ├── .gitignore ├── keys.json.template ├── CMakeLists.txt ├── src ├── CMakeLists.txt ├── key_store.cc ├── main_syscon.cc ├── patch.cc ├── main_eapk.cc ├── main_eap.cc ├── main_emc.cc ├── common.cc ├── syscon.cc ├── emc.cc ├── eap.cc └── crypto.cc ├── .clang-format └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Al Azif, https://github.com/Al-Azif/ps4-cfw-toolkit 2 | 3 | All rights reserved, contact for licensing and distribution requests. 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/banned"] 2 | path = external/banned 3 | url = https://github.com/x509cert/banned 4 | branch = master 5 | [submodule "external/json"] 6 | path = external/json 7 | url = https://github.com/nlohmann/json 8 | -------------------------------------------------------------------------------- /include/cmake_vars_eap.h.in: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_H_ 2 | #define VERSION_H_ 3 | 4 | #include 5 | 6 | const inline std::string g_AppVersion{"@eap_VERSION@"}; 7 | const inline std::string g_AppDescription{"@eap_DESCRIPTION@"}; 8 | const inline std::string g_AppHomepage{"@eap_HOMEPAGE_URL@"}; 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /include/cmake_vars_emc.h.in: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_H_ 2 | #define VERSION_H_ 3 | 4 | #include 5 | 6 | const inline std::string g_AppVersion{"@emc_VERSION@"}; 7 | const inline std::string g_AppDescription{"@emc_DESCRIPTION@"}; 8 | const inline std::string g_AppHomepage{"@emc_HOMEPAGE_URL@"}; 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /include/cmake_vars_eapk.h.in: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_H_ 2 | #define VERSION_H_ 3 | 4 | #include 5 | 6 | const inline std::string g_AppVersion{"@eapk_VERSION@"}; 7 | const inline std::string g_AppDescription{"@eapk_DESCRIPTION@"}; 8 | const inline std::string g_AppHomepage{"@eapk_HOMEPAGE_URL@"}; 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /include/cmake_vars_syscon.h.in: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_H_ 2 | #define VERSION_H_ 3 | 4 | #include 5 | 6 | const inline std::string g_AppVersion{"@syscon_VERSION@"}; 7 | const inline std::string g_AppDescription{"@syscon_DESCRIPTION@"}; 8 | const inline std::string g_AppHomepage{"@syscon_HOMEPAGE_URL@"}; 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "intelliSenseMode": "clang-x64", 6 | "includePath": [ 7 | "${workspaceFolder}/external/banned/*", 8 | "${workspaceFolder}/external/json/single_include/*", 9 | "${workspaceFolder}/include/*" 10 | ], 11 | "defines": [], 12 | "compilerPath": "/usr/bin/clang++", 13 | "cStandard": "c17", 14 | "cppStandard": "c++17" 15 | } 16 | ], 17 | "version": 4 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # NEVER include this file 2 | /keys.json 3 | 4 | # IDE configs 5 | /.vscode/* 6 | !/.vscode/c_cpp_properties.json 7 | !/.vscode/tasks.json 8 | 9 | # Testing data 10 | /flawfinder_src.log 11 | /flawfinder_include.log 12 | /cppcheck_src.log 13 | /cppcheck_include.log 14 | 15 | # Build artifacts 16 | /cmake_install.cmake 17 | /CMakeCache.txt 18 | /CMakeFiles/ 19 | /Makefile 20 | /include/cmake_vars_eap.h 21 | /include/cmake_vars_eapk.h 22 | /include/cmake_vars_emc.h 23 | /include/cmake_vars_syscon.h 24 | 25 | /src/cmake_install.cmake 26 | /src/CMakeCache.txt 27 | /src/CMakeFiles/ 28 | /src/Makefile 29 | /src/libCFW.a 30 | 31 | /bin/ 32 | -------------------------------------------------------------------------------- /include/syscon.h: -------------------------------------------------------------------------------- 1 | #ifndef SYSCON_H_ 2 | #define SYSCON_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace syscon { 8 | bool Decrypt(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 9 | bool Encrypt(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 10 | 11 | const char c_SysconBlnkMagic_[0x4]{'\x42', '\x4C', '\x4E', '\x4B'}; 12 | const char c_SysconBaseMagic_[0x4]{'\x42', '\x41', '\x53', '\x45'}; 13 | const char c_SysconSystMagic_[0x4]{'\x53', '\x59', '\x53', '\x54'}; 14 | const char c_SysconPtchMagic_[0x4]{'\x50', '\x54', '\x43', '\x48'}; 15 | } // namespace syscon 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/patch.h: -------------------------------------------------------------------------------- 1 | #ifndef PATCH_H_ 2 | #define PATCH_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace patch { 8 | bool Offset(unsigned char *p_Input, size_t p_InputLen, const char *p_Patch, size_t p_PatchLen, size_t p_Offset); 9 | uint64_t Pattern(unsigned char *p_Input, size_t p_InputLen, const char *p_Search, size_t p_SearchLen, const char *p_Replace, size_t p_ReplaceLen, size_t p_Occurances = 0); 10 | uint64_t PatternStartAt(unsigned char *p_Input, size_t p_InputLen, const char *p_Search, size_t p_SearchLen, const char *p_Replace, size_t p_ReplaceLen, size_t p_StartAddr, size_t p_Occurances = 0); 11 | uint64_t PatternEndAt(unsigned char *p_Input, size_t p_InputLen, const char *p_Search, size_t p_SearchLen, const char *p_Replace, size_t p_ReplaceLen, size_t p_EndAddr, size_t p_Occurances = 0); 12 | uint64_t PatternBetween(unsigned char *p_Input, size_t p_InputLen, const char *p_Search, size_t p_SearchLen, const char *p_Replace, size_t p_ReplaceLen, size_t p_StartAddr, size_t p_EndAddr, size_t p_Occurances = 0); 13 | } // namespace patch 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H_ 2 | #define COMMON_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define MAX_PATH 255 13 | 14 | #ifdef _WIN32 15 | #define OS_SEP '\\' 16 | #define CONST_OS_SEP "\\" 17 | #else 18 | #define OS_SEP '/' 19 | #define CONST_OS_SEP "/" 20 | #endif 21 | 22 | template 23 | inline std::string hex(T x) { 24 | std::stringstream s_HexAssembler; 25 | s_HexAssembler << std::uppercase << std::hex << std::setw(sizeof(T) * 2) << std::setfill('0') << static_cast(x); 26 | std::string s_Output = s_HexAssembler.str(); 27 | s_Output.erase(0, std::min(s_Output.find_first_not_of("00"), s_Output.size() - 2)); 28 | return "0x" + s_Output; 29 | } 30 | 31 | bool CheckMagic(const unsigned char *p_Input, const char *p_Magic, size_t p_MagicLen); 32 | bool IsEmptyString(const std::string &p_InputString); 33 | bool WriteFile(const unsigned char *p_Input, size_t p_InputLen, const std::string &p_OutputPath, int64_t p_Offset = 0); 34 | bool EndsWith(const std::string &p_Input, const std::string &p_Match); 35 | std::string HexDump(const void *p_Pointer, uint32_t p_Len); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /keys.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "EAP": { 3 | "KERNEL_ENC_KEY": { 4 | "0": "", 5 | "1": "", 6 | "2": "", 7 | "3": "" 8 | }, 9 | "KERNEL_MAC_KEY": { 10 | "0": "", 11 | "1": "", 12 | "2": "", 13 | "3": "" 14 | }, 15 | "KBL_AES_KEY": { 16 | "AEOLIA": "", 17 | "BELIZE": "", 18 | "BELIZE 2": "", 19 | "BAIKAL": "" 20 | }, 21 | "KBL_IV": { 22 | "AEOLIA": "", 23 | "BELIZE": "", 24 | "BELIZE 2": "", 25 | "BAIKAL": "" 26 | }, 27 | "KBL_MAC_KEY": { 28 | "AEOLIA": "", 29 | "BELIZE": "", 30 | "BELIZE 2": "", 31 | "BAIKAL": "" 32 | } 33 | }, 34 | "EMC": { 35 | "IPL_AES_KEY": { 36 | "AEOLIA": "", 37 | "BELIZE": "", 38 | "BELIZE 2": "", 39 | "BAIKAL": "" 40 | }, 41 | "IPL_IV": { 42 | "AEOLIA": "", 43 | "BELIZE": "", 44 | "BELIZE 2": "", 45 | "BAIKAL": "" 46 | }, 47 | "IPL_MAC_KEY": { 48 | "AEOLIA": "", 49 | "BELIZE": "", 50 | "BELIZE 2": "", 51 | "BAIKAL": "" 52 | } 53 | }, 54 | "SYSCON": { 55 | "FULL_AES_KEY": "", 56 | "FULL_IV": "", 57 | "FULL_MAC_KEY": "", 58 | "PATCH_AES_KEY": "", 59 | "PATCH_IV": "", 60 | "PATCH_MAC_KEY": "" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /include/emc.h: -------------------------------------------------------------------------------- 1 | #ifndef EMC_H_ 2 | #define EMC_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace emc { 12 | typedef struct { 13 | unsigned char magic[0x4]; 14 | uint16_t version; 15 | uint16_t type; 16 | uint32_t header_size; 17 | uint32_t body_size; 18 | uint32_t entry_point; 19 | uint32_t base_address; 20 | unsigned char fill_pattern[0x10]; 21 | unsigned char key_seed[0x8]; 22 | unsigned char body_aes_key[0x10]; 23 | unsigned char body_hmac_key[0x10]; 24 | unsigned char body_hmac[SHA_DIGEST_LENGTH]; 25 | struct { 26 | uint64_t : 64; 27 | }; // 0x8 Padding 28 | unsigned char header_hmac[SHA_DIGEST_LENGTH]; 29 | } EmcIplHeader; 30 | 31 | // This isn't used but it used as a shortcut for it's size 32 | typedef struct { 33 | unsigned char body_aes_key[0x10]; 34 | unsigned char body_hmac_key[0x10]; 35 | unsigned char body_hmac[SHA_DIGEST_LENGTH]; 36 | struct { 37 | uint64_t : 64; 38 | }; // 0x8 Padding 39 | unsigned char header_hmac[SHA_DIGEST_LENGTH]; 40 | } EmcIplEncryptionHeader; 41 | 42 | bool IsEmcIpl(const unsigned char *p_Input, size_t p_InputLen); 43 | bool Decrypt(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 44 | bool Encrypt(const unsigned char *p_Input, size_t p_InputLen, std::string p_SouthbridgeRevision, std::vector &p_Output); 45 | 46 | const char c_EmcMagic_[0x4]{'\xAA', '\xF9', '\x8F', '\xD4'}; 47 | const char c_FillPattern_[0x10]{'\xDE', '\xAD', '\xBE', '\xEF', '\xCA', '\xFE', '\xBE', '\xBE', '\xDE', '\xAF', '\xBE', '\xEF', '\xCA', '\xFE', '\xBE', '\xBE'}; 48 | const char c_KeySeed_[0x8]{'\xF1', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7', '\xF8'}; 49 | } // namespace emc 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /include/key_store.h: -------------------------------------------------------------------------------- 1 | #ifndef KEY_STORE_H_ 2 | #define KEY_STORE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "nlohmann/json.hpp" 10 | 11 | extern nlohmann::json g_KeyStore; 12 | 13 | // Allow for overloaded macros: https://stackoverflow.com/a/16683147 14 | #define CAT(A, B) A##B 15 | #define SELECT(NAME, NUM) CAT(NAME##_, NUM) 16 | #define GET_COUNT(_1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ...) COUNT 17 | #define VA_SIZE(...) GET_COUNT(__VA_ARGS__, 6, 5, 4, 3, 2, 1) 18 | #define VA_SELECT(NAME, ...) \ 19 | SELECT(NAME, VA_SIZE(__VA_ARGS__)) \ 20 | (__VA_ARGS__) 21 | 22 | // Overloaded key helper macros 23 | #define GetKey(...) VA_SELECT(GetKey, __VA_ARGS__) 24 | #define GetKey_2(category, key) &GetKeyData(category, key)[0] 25 | #define GetKey_3(category, key, key_index) &GetKeyData(category, key, key_index)[0] 26 | 27 | #define GetKeyToVariable(...) VA_SELECT(GetKeyToVariable, __VA_ARGS__) 28 | #define GetKeyToVariable_3(category, key, variable) \ 29 | unsigned char variable[GetKeySize(category, key)]; \ 30 | std::memcpy(variable, GetKey_2(category, key), sizeof(variable)); 31 | #define GetKeyToVariable_4(category, key, key_index, variable) \ 32 | unsigned char variable[GetKeySize(category, key, key_index)]; \ 33 | std::memcpy(variable, GetKey_3(category, key, key_index), sizeof(variable)); 34 | 35 | bool InitializeKeyStore(const std::string &p_Path); 36 | size_t GetKeySize(const std::string &p_KeyCategory, const std::string &p_KeyName); 37 | size_t GetKeySize(const std::string &p_KeyCategory, const std::string &p_KeyName, uint64_t p_KeyIndex); 38 | size_t GetKeySize(const std::string &p_KeyCategory, const std::string &p_KeyName, const std::string &p_KeyIndex); 39 | std::vector GetKeyData(const std::string &p_KeyCategory, const std::string &p_KeyName); 40 | std::vector GetKeyData(const std::string &p_KeyCategory, const std::string &p_KeyName, uint64_t p_KeyIndex); 41 | std::vector GetKeyData(const std::string &p_KeyCategory, const std::string &p_KeyName, const std::string &p_KeyIndex); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set the minimum version of cmake required 2 | cmake_minimum_required(VERSION 3.10.2) 3 | 4 | # Set the project name 5 | project(CFW_TOOLKIT) 6 | 7 | # Generate binary files in `/bin` directory 8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 9 | 10 | # Set to Debug mode by default and echo set value 11 | if(NOT CMAKE_BUILD_TYPE) 12 | set(CMAKE_BUILD_TYPE "Debug") 13 | endif() 14 | if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") 15 | if(NOT CMAKE_BUILD_TYPE STREQUAL "Release") 16 | message(FATAL_ERROR "Please specify a build type of either \"Release\" or \"Debug\"\nCurrent Value: ${CMAKE_BUILD_TYPE}") 17 | endif() 18 | endif() 19 | message("-- Build Type: ${CMAKE_BUILD_TYPE}") 20 | 21 | # Require C++17 or greater 22 | if(NOT CMAKE_CXX_STANDARD) 23 | set(CMAKE_CXX_STANDARD 17) 24 | endif() 25 | 26 | if(CMAKE_CXX_STANDARD LESS 17) 27 | message(FATAL_ERROR "Incompatible with C++${CMAKE_CXX_STANDARD}. Requires C++17.") 28 | endif() 29 | 30 | # No GNU 31 | set(CMAKE_CXX_EXTENSIONS OFF) 32 | 33 | # TODO: Include glog 34 | # find_package(glog REQUIRED) 35 | 36 | # Include gflags 37 | find_package(gflags REQUIRED) 38 | 39 | # Include OpenSSL 40 | set(OPENSSL_USE_STATIC_LIBS TRUE) 41 | find_package(OpenSSL REQUIRED) 42 | 43 | # Setup compiler flags 44 | if(NOT MSVC) 45 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -D_SDL_BANNED_RECOMMENDED=true") 46 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 47 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g") 48 | elseif(CMAKE_BUILD_TYPE STREQUAL "Release") 49 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG") 50 | endif() 51 | else() 52 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wall /WX /D_SDL_BANNED_RECOMMENDED=true") 53 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 54 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Od /Z7") 55 | elseif(CMAKE_BUILD_TYPE STREQUAL "Release") 56 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Ox -DNDEBUG") 57 | endif() 58 | endif() 59 | 60 | include_directories(${CMAKE_SOURCE_DIR}/external/banned) 61 | include_directories(${CMAKE_SOURCE_DIR}/external/json/single_include) 62 | include_directories(${CMAKE_SOURCE_DIR}/include) 63 | include_directories(/usr/local/include) 64 | 65 | add_subdirectory(${CMAKE_SOURCE_DIR}/src) 66 | -------------------------------------------------------------------------------- /include/crypto.h: -------------------------------------------------------------------------------- 1 | #ifndef CRYPTO_H_ 2 | #define CRYPTO_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | template 11 | inline I align_up(I x, I align = 16); 12 | template 13 | inline I align_down(I x, I align = 16); 14 | 15 | bool RsaPkcs1V15Verify(const unsigned char *p_Modulus, size_t p_ModulusLen, const unsigned char *p_PublicExponent, size_t p_PublicExponentLen, const unsigned char *p_Input, size_t p_InputLen, const unsigned char *p_Signature, size_t p_SignatureLen); 16 | bool RsaPublicEncrypt(const unsigned char *p_Modulus, size_t p_ModulusLen, const unsigned char *p_PublicExponent, size_t p_PublicExponentLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, size_t p_OutputLen, int32_t p_Padding = RSA_NO_PADDING); 17 | 18 | size_t AesEncryptEcb(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, int32_t p_Padding = 0); 19 | size_t AesDecryptEcb(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, int32_t p_Padding = 0); 20 | size_t AesEncryptCbc(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Iv, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, int32_t p_Padding = 0); 21 | size_t AesDecryptCbc(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Iv, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, int32_t p_Padding = 0); 22 | size_t AesEncryptCbcCts(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Iv, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 23 | size_t AesDecryptCbcCts(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Iv, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 24 | 25 | bool Sha256(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 26 | bool HmacSha1(const unsigned char *p_Key, int32_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 27 | bool HmacSha256(const unsigned char *p_Key, int32_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 28 | bool Cmac(const unsigned char *p_Key, int32_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/eap.h: -------------------------------------------------------------------------------- 1 | #ifndef EAP_H_ 2 | #define EAP_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace eap { 12 | typedef struct { 13 | unsigned char magic[0x4]; 14 | uint16_t version; 15 | uint16_t type; 16 | uint32_t header_size; 17 | uint32_t body_size; 18 | uint32_t entry_point; 19 | uint32_t base_address; 20 | unsigned char fill_pattern[0x10]; 21 | unsigned char key_seed[0x8]; 22 | unsigned char body_aes_key[0x10]; 23 | unsigned char body_hmac_key[0x10]; 24 | unsigned char body_hmac[SHA_DIGEST_LENGTH]; 25 | struct { 26 | uint64_t : 64; 27 | }; // 0x8 Padding 28 | unsigned char header_hmac[SHA_DIGEST_LENGTH]; 29 | } EapKblHeader; 30 | 31 | // This isn't used but it used as a shortcut for it's size, make sure it 32 | // matches the body onward of the struct above it 33 | typedef struct { 34 | unsigned char body_aes_key[0x10]; 35 | unsigned char body_hmac_key[0x10]; 36 | unsigned char body_hmac[SHA_DIGEST_LENGTH]; 37 | struct { 38 | uint64_t : 64; 39 | }; // 0x8 Padding 40 | unsigned char header_hmac[SHA_DIGEST_LENGTH]; 41 | } EapKblEncryptionHeader; 42 | 43 | typedef struct { 44 | unsigned char magic[0x4]; 45 | uint32_t version; 46 | unsigned char iv[0x10]; 47 | unsigned char digest[SHA_DIGEST_LENGTH]; 48 | } EapKernelHeader; 49 | 50 | typedef struct { 51 | unsigned char magic[0x4]; 52 | uint32_t size; 53 | uint32_t offset; 54 | } EapKernelBodyInfo; 55 | 56 | bool IsEapKbl(const unsigned char *p_Input, size_t p_InputLen); 57 | bool IsEapKernel(const unsigned char *p_Input, size_t p_InputLen); 58 | bool DecryptKbl(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 59 | bool DecryptKernel(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output); 60 | bool EncryptKbl(const unsigned char *p_Input, size_t p_InputLen, std::string p_SouthbridgeRevision, std::vector &p_Output); 61 | bool EncryptKernel(const unsigned char *p_Input, size_t p_InputLen, uint32_t p_KeysetNumber, std::vector &p_Output); 62 | 63 | const char c_EapElfMagic_[0x4]{'\x7F', '\x45', '\x4C', '\x46'}; 64 | 65 | const char c_EapKblMagic_[0x4]{'\xAA', '\xF9', '\x8F', '\xD4'}; 66 | const char c_EapKernelMagic_[0x4]{'\x5C', '\xC9', '\xEB', '\x12'}; 67 | const char c_EapBodyInfoHeaderMagic_[0x4]{'\x00', '\x6E', '\x72', '\x4B'}; 68 | const char c_EapBodyHeaderMagic_[0x4]{'\x5C', '\xC9', '\xEB', '\x12'}; 69 | 70 | const char c_FillPattern_[0x10]{'\xDE', '\xAD', '\xBE', '\xEF', '\xCA', '\xFE', '\xBE', '\xBE', '\xDE', '\xAF', '\xBE', '\xEF', '\xCA', '\xFE', '\xBE', '\xBE'}; 71 | const char c_KeySeed_[0x8]{'\xF1', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7', '\xF8'}; 72 | 73 | const uint32_t c_ExpectedVersion_{0x10000}; 74 | const uint16_t c_SectorSize_{0x200}; 75 | } // namespace eap 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.2) 2 | 3 | add_library(CFW 4 | ${CMAKE_SOURCE_DIR}/src/common.cc 5 | ${CMAKE_SOURCE_DIR}/src/crypto.cc 6 | ${CMAKE_SOURCE_DIR}/src/eap.cc 7 | ${CMAKE_SOURCE_DIR}/src/emc.cc 8 | ${CMAKE_SOURCE_DIR}/src/key_store.cc 9 | ${CMAKE_SOURCE_DIR}/src/patch.cc 10 | ${CMAKE_SOURCE_DIR}/src/syscon.cc 11 | ) 12 | 13 | # EAP KBL 14 | project(eap 15 | VERSION 0.0.1 16 | DESCRIPTION "Decrypts/Encrypts EAP KBL (Kernel Boot Loader) images." 17 | HOMEPAGE_URL "https://github.com/Al-Azif/ps4-cfw-toolkit" 18 | LANGUAGES CXX 19 | ) 20 | 21 | configure_file(${CMAKE_SOURCE_DIR}/include/cmake_vars_eap.h.in ${CMAKE_SOURCE_DIR}/include/cmake_vars_eap.h @ONLY) 22 | 23 | add_executable(eap ${CMAKE_SOURCE_DIR}/src/main_eap.cc) 24 | target_link_libraries(eap CFW) 25 | 26 | target_link_libraries(eap -lglog) # TODO 27 | target_link_libraries(eap gflags) 28 | target_link_libraries(eap OpenSSL::SSL) 29 | target_link_libraries(eap OpenSSL::Crypto) 30 | 31 | # EAP Kernel 32 | project(eapk 33 | VERSION 0.0.1 34 | DESCRIPTION "Decrypts/Encrypts EAP Kernel images." 35 | HOMEPAGE_URL "https://github.com/Al-Azif/ps4-cfw-toolkit" 36 | LANGUAGES CXX 37 | ) 38 | 39 | configure_file(${CMAKE_SOURCE_DIR}/include/cmake_vars_eapk.h.in ${CMAKE_SOURCE_DIR}/include/cmake_vars_eapk.h @ONLY) 40 | 41 | add_executable(eapk ${CMAKE_SOURCE_DIR}/src/main_eapk.cc) 42 | target_link_libraries(eapk CFW) 43 | 44 | target_link_libraries(eapk -lglog) # TODO 45 | target_link_libraries(eapk gflags) 46 | target_link_libraries(eapk OpenSSL::SSL) 47 | target_link_libraries(eapk OpenSSL::Crypto) 48 | 49 | # EMC 50 | project(emc 51 | VERSION 0.0.1 52 | DESCRIPTION "Decrypts/Encrypts EMC images. Can apply \\\"Godmode\\\" patches during either operation." 53 | HOMEPAGE_URL "https://github.com/Al-Azif/ps4-cfw-toolkit" 54 | LANGUAGES CXX 55 | ) 56 | 57 | configure_file(${CMAKE_SOURCE_DIR}/include/cmake_vars_emc.h.in ${CMAKE_SOURCE_DIR}/include/cmake_vars_emc.h @ONLY) 58 | 59 | add_executable(emc ${CMAKE_SOURCE_DIR}/src/main_emc.cc) 60 | target_link_libraries(emc CFW) 61 | 62 | target_link_libraries(emc -lglog) # TODO 63 | target_link_libraries(emc gflags) 64 | target_link_libraries(emc OpenSSL::SSL) 65 | target_link_libraries(emc OpenSSL::Crypto) 66 | 67 | # SYSCON 68 | project(syscon 69 | VERSION 0.0.1 70 | DESCRIPTION "Decrypts/Encrypts SYSCON images." 71 | HOMEPAGE_URL "https://github.com/Al-Azif/ps4-cfw-toolkit" 72 | LANGUAGES CXX 73 | ) 74 | 75 | configure_file(${CMAKE_SOURCE_DIR}/include/cmake_vars_syscon.h.in ${CMAKE_SOURCE_DIR}/include/cmake_vars_syscon.h @ONLY) 76 | 77 | add_executable(syscon ${CMAKE_SOURCE_DIR}/src/main_syscon.cc) 78 | target_link_libraries(syscon CFW) 79 | 80 | target_link_libraries(syscon -lglog) # TODO 81 | target_link_libraries(syscon gflags) 82 | target_link_libraries(syscon OpenSSL::SSL) 83 | target_link_libraries(syscon OpenSSL::Crypto) 84 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build debug", 6 | "type": "shell", 7 | "command": "cmake -DCMAKE_BUILD_TYPE=Debug . && make -j6", 8 | "group": { 9 | "kind": "build", 10 | "isDefault": true 11 | }, 12 | "presentation": { 13 | "reveal": "always", 14 | "focus": true, 15 | "panel": "shared", 16 | "clear": true 17 | } 18 | }, 19 | { 20 | "label": "Build release", 21 | "type": "shell", 22 | "command": "cmake -DCMAKE_BUILD_TYPE=Release . && make -j6", 23 | "group": "build", 24 | "presentation": { 25 | "reveal": "always", 26 | "focus": true, 27 | "panel": "shared", 28 | "clear": true 29 | } 30 | }, 31 | { 32 | "label": "Cleanup Build", 33 | "type": "shell", 34 | "command": "rm -rf bin/ CMakeFiles/ src/CMakeFiles/ cmake_install.cmake src/cmake_install.cmake CMakeCache.txt src/CMakeCache.txt Makefile src/Makefile include/cmake_vars_eap.h include/cmake_vars_eapk.h include/cmake_vars_emc.h include/cmake_vars_syscon.h src/libCFW.a", 35 | "group": "build", 36 | "presentation": { 37 | "reveal": "never", 38 | "panel": "shared", 39 | "close": true 40 | } 41 | }, 42 | { 43 | "label": "Run clang-format", 44 | "type": "shell", 45 | "command": "find ./src -regex '.*\\.\\(c\\|cc\\|cpp\\|h\\|hpp\\)' -exec clang-format -style=file -i {} \\; || true && find ./include -regex '.*\\.\\(c\\|cc\\|cpp\\|h\\|hpp\\)' -exec clang-format -style=file -i {} \\; || true", 46 | "group": "test", 47 | "presentation": { 48 | "reveal": "never", 49 | "panel": "shared", 50 | "close": true 51 | } 52 | }, 53 | { 54 | "label": "Run Flawfinder", 55 | "type": "shell", 56 | "command": "flawfinder src/ > flawfinder_src.log 2>&1 || true && flawfinder include/ > flawfinder_include.log 2>&1 || true", 57 | "group": "test", 58 | "presentation": { 59 | "reveal": "never", 60 | "panel": "shared", 61 | "close": true 62 | } 63 | }, 64 | { 65 | "label": "Run Cppcheck", 66 | "type": "shell", 67 | "command": "cppcheck src/ > cppcheck_src.log 2>&1 || true && cppcheck include/ > cppcheck_include.log 2>&1 || true", 68 | "group": "test", 69 | "presentation": { 70 | "reveal": "never", 71 | "panel": "shared", 72 | "close": true 73 | } 74 | }, 75 | { 76 | "label": "Run Lint/Checks", 77 | "dependsOrder": "sequence", 78 | "dependsOn": [ 79 | "Run clang-format", 80 | "Run Flawfinder", 81 | "Run Cppcheck" 82 | ], 83 | "group": { 84 | "kind": "test", 85 | "isDefault": true 86 | }, 87 | "presentation": { 88 | "reveal": "never", 89 | "panel": "shared", 90 | "close": true 91 | } 92 | } 93 | ] 94 | } 95 | -------------------------------------------------------------------------------- /src/key_store.cc: -------------------------------------------------------------------------------- 1 | #include "key_store.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "nlohmann/json.hpp" 15 | 16 | nlohmann::json g_KeyStore; 17 | 18 | bool InitializeKeyStore(const std::string &p_Path) { 19 | if (std::filesystem::exists(p_Path) && !std::filesystem::is_regular_file(p_Path)) { 20 | LOG(ERROR) << "Key store path exists, but is not a file: " << p_Path; 21 | return false; 22 | } 23 | 24 | std::ifstream s_InputFile(p_Path, std::ios::in); 25 | if (!s_InputFile || !s_InputFile.good()) { 26 | LOG(ERROR) << "Key store file could not be read: " << p_Path; 27 | s_InputFile.close(); 28 | return false; 29 | } 30 | 31 | g_KeyStore = nlohmann::json::parse(s_InputFile); 32 | return true; 33 | } 34 | 35 | size_t GetKeySize(const std::string &p_KeyCategory, const std::string &p_KeyName) { 36 | std::string s_KeyString{g_KeyStore[p_KeyCategory][p_KeyName]}; 37 | 38 | size_t s_Return{s_KeyString.size()}; 39 | if (s_Return != 0 && s_Return % 2 != 0) { 40 | s_Return++; 41 | } 42 | return s_Return / 2; 43 | } 44 | 45 | size_t GetKeySize(const std::string &p_KeyCategory, const std::string &p_KeyName, uint64_t p_KeyIndex) { 46 | std::stringstream s_StringAssembler; 47 | s_StringAssembler << p_KeyIndex; 48 | 49 | return GetKeySize(p_KeyCategory, p_KeyName, s_StringAssembler.str()); 50 | } 51 | 52 | size_t GetKeySize(const std::string &p_KeyCategory, const std::string &p_KeyName, const std::string &p_KeyIndex) { 53 | std::string s_KeyString{g_KeyStore[p_KeyCategory][p_KeyName][p_KeyIndex]}; 54 | 55 | size_t s_Return{s_KeyString.size()}; 56 | if (s_Return != 0 && s_Return % 2 != 0) { 57 | s_Return++; 58 | } 59 | return s_Return / 2; 60 | } 61 | 62 | std::vector GetKeyData(const std::string &p_KeyCategory, const std::string &p_KeyName) { 63 | std::vector s_KeyVector; 64 | 65 | std::string s_KeyString{g_KeyStore[p_KeyCategory][p_KeyName]}; 66 | for (size_t i{0}; i < s_KeyString.length(); i += 2) { 67 | std::string sByteString{s_KeyString.substr(i, 2)}; 68 | char s_Byte{(char)strtol(sByteString.c_str(), NULL, 16)}; 69 | s_KeyVector.push_back(s_Byte); 70 | } 71 | 72 | return s_KeyVector; 73 | } 74 | 75 | std::vector GetKeyData(const std::string &p_KeyCategory, const std::string &p_KeyName, uint64_t p_KeyIndex) { 76 | std::stringstream s_StringAssembler; 77 | s_StringAssembler << p_KeyIndex; 78 | 79 | return GetKeyData(p_KeyCategory, p_KeyName, s_StringAssembler.str()); 80 | } 81 | 82 | std::vector GetKeyData(const std::string &p_KeyCategory, const std::string &p_KeyName, const std::string &p_KeyIndex) { 83 | std::vector s_KeyVector; 84 | 85 | std::string s_KeyString{g_KeyStore[p_KeyCategory][p_KeyName][p_KeyIndex]}; 86 | for (size_t i{0}; i < s_KeyString.length(); i += 2) { 87 | std::string s_ByteString{s_KeyString.substr(i, 2)}; 88 | char s_Byte{(char)strtol(s_ByteString.c_str(), NULL, 16)}; 89 | s_KeyVector.push_back(s_Byte); 90 | } 91 | 92 | return s_KeyVector; 93 | } 94 | -------------------------------------------------------------------------------- /src/main_syscon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "cmake_vars_syscon.h" 11 | #include "common.h" 12 | #include "key_store.h" 13 | #include "syscon.h" 14 | 15 | #include "banned.h" 16 | 17 | DEFINE_bool(decrypt, false, "Run in decryption mode"); 18 | DEFINE_bool(encrypt, false, "Run in encryption mode"); 19 | DEFINE_string(input, "40000001", "Path of the SYSCON file to load"); 20 | DEFINE_string(keys, "keys.json", "Path of the key file to load"); 21 | DEFINE_string(output, "40000001.modified", "Path to save the output SYSCON file"); 22 | 23 | int main(int argc, char *argv[]) { 24 | gflags::SetUsageMessage(g_AppDescription); 25 | gflags::SetVersionString(g_AppVersion); 26 | 27 | gflags::ParseCommandLineFlags(&argc, &argv, true); 28 | 29 | google::InitGoogleLogging(argv[0]); 30 | google::InstallFailureSignalHandler(); 31 | 32 | if (!InitializeKeyStore(FLAGS_keys)) { 33 | LOG(FATAL) << "Could not read key file: " << FLAGS_keys; 34 | return 1; 35 | } 36 | 37 | if (FLAGS_decrypt && FLAGS_encrypt) { 38 | LOG(FATAL) << "Cannot use both \"encrypt\" or \"decrypt\" flag at the same time"; 39 | return 1; 40 | } 41 | if (!FLAGS_decrypt && !FLAGS_encrypt) { 42 | LOG(FATAL) << "Must use \"encrypt\" or \"decrypt\" flag"; 43 | return 1; 44 | } 45 | bool s_Encrypt{false}; 46 | if (FLAGS_encrypt) { 47 | s_Encrypt = true; 48 | } 49 | 50 | if (FLAGS_input == FLAGS_output) { 51 | LOG(FATAL) << "Input and output path cannot be the same"; 52 | return 1; 53 | } 54 | 55 | std::filesystem::path s_InputPath(FLAGS_input); 56 | std::filesystem::path s_OutputPath(FLAGS_output); 57 | if (FLAGS_input != "40000001" && FLAGS_output == "40000001.modified") { 58 | s_OutputPath = s_InputPath; 59 | s_OutputPath += ".modified"; 60 | } 61 | 62 | gflags::ShutDownCommandLineFlags(); 63 | 64 | LOG(INFO) << "Initalizing..."; 65 | 66 | std::ifstream s_InputFile(s_InputPath, std::ios::in | std::ios::binary); 67 | if (!s_InputFile || !s_InputFile.good()) { 68 | s_InputFile.close(); 69 | LOG(FATAL) << "Unable to open input file " << s_InputPath; 70 | return 1; 71 | } 72 | uint64_t s_InputLen{std::filesystem::file_size(s_InputPath)}; 73 | 74 | std::vector s_InputData(s_InputLen); 75 | if (!s_InputFile.read(reinterpret_cast(&s_InputData[0]), s_InputData.size()).good()) { // Flawfinder: ignore 76 | s_InputFile.close(); 77 | LOG(FATAL) << "Unable to read input file " << s_InputPath; 78 | return 1; 79 | } 80 | s_InputFile.close(); 81 | 82 | std::vector s_OutputData; 83 | if (s_Encrypt) { 84 | if (!syscon::Encrypt(&s_InputData[0], s_InputLen, s_OutputData)) { 85 | LOG(FATAL) << "Failed to encrypt syscon file"; 86 | return 1; 87 | } 88 | } else { 89 | if (!syscon::Decrypt(&s_InputData[0], s_InputLen, s_OutputData)) { 90 | LOG(FATAL) << "Failed to decrypt syscon file"; 91 | return 1; 92 | } 93 | } 94 | 95 | if (s_OutputData.size() == 0) { 96 | LOG(FATAL) << "Failed to modify syscon file"; 97 | return 1; 98 | } 99 | 100 | if (!WriteFile(&s_OutputData[0], s_OutputData.size(), s_OutputPath)) { 101 | LOG(FATAL) << "Failed to write output to file to " << s_OutputPath; 102 | return 1; 103 | } 104 | 105 | LOG(INFO) << "Done, file saved to " << s_OutputPath; 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: false 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 4294967295 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: true 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 65 | Priority: 2 66 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 67 | Priority: 3 68 | - Regex: '.*' 69 | Priority: 1 70 | IncludeIsMainRegex: '(Test)?$' 71 | IndentCaseLabels: false 72 | IndentPPDirectives: None 73 | IndentWidth: 2 74 | IndentWrappedFunctionNames: false 75 | JavaScriptQuotes: Leave 76 | JavaScriptWrapImports: true 77 | KeepEmptyLinesAtTheStartOfBlocks: true 78 | MacroBlockBegin: '' 79 | MacroBlockEnd: '' 80 | MaxEmptyLinesToKeep: 1 81 | NamespaceIndentation: None 82 | ObjCBlockIndentWidth: 2 83 | ObjCSpaceAfterProperty: false 84 | ObjCSpaceBeforeProtocolList: true 85 | PenaltyBreakAssignment: 2 86 | PenaltyBreakBeforeFirstCallParameter: 19 87 | PenaltyBreakComment: 300 88 | PenaltyBreakFirstLessLess: 120 89 | PenaltyBreakString: 1000 90 | PenaltyExcessCharacter: 1000000 91 | PenaltyReturnTypeOnItsOwnLine: 60 92 | PointerAlignment: Right 93 | RawStringFormats: 94 | - Delimiter: pb 95 | Language: TextProto 96 | BasedOnStyle: google 97 | ReflowComments: true 98 | SortIncludes: true 99 | SortUsingDeclarations: true 100 | SpaceAfterCStyleCast: false 101 | SpaceAfterTemplateKeyword: true 102 | SpaceBeforeAssignmentOperators: true 103 | SpaceBeforeParens: ControlStatements 104 | SpaceInEmptyParentheses: false 105 | SpacesBeforeTrailingComments: 1 106 | SpacesInAngles: false 107 | SpacesInContainerLiterals: true 108 | SpacesInCStyleCastParentheses: false 109 | SpacesInParentheses: false 110 | SpacesInSquareBrackets: false 111 | Standard: Cpp11 112 | TabWidth: 2 113 | UseTab: Never 114 | ... 115 | -------------------------------------------------------------------------------- /src/patch.cc: -------------------------------------------------------------------------------- 1 | #include "patch.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "common.h" 13 | 14 | #include "banned.h" 15 | 16 | namespace patch { 17 | bool Offset(unsigned char *p_Input, size_t p_InputLen, const char *p_Patch, size_t p_PatchLen, size_t p_Offset) { 18 | if (p_InputLen < p_PatchLen + p_Offset) { 19 | LOG(ERROR) << "Patch must not overflow input"; 20 | return false; 21 | } 22 | 23 | std::copy(p_Patch, p_Patch + p_PatchLen, p_Input + p_Offset); 24 | 25 | return true; 26 | } 27 | 28 | uint64_t Pattern(unsigned char *p_Input, size_t p_InputLen, const char *p_Search, size_t p_SearchLen, const char *p_Replace, size_t p_ReplaceLen, size_t p_Occurances) { 29 | if (p_InputLen == 0 || p_SearchLen == 0 || p_ReplaceLen == 0) { 30 | return 0; 31 | } 32 | if (p_InputLen < p_ReplaceLen) { 33 | LOG(ERROR) << "Patch must not overflow input"; 34 | return 0; 35 | } 36 | 37 | size_t s_Limit{std::max(p_SearchLen, p_ReplaceLen)}; 38 | size_t s_HitCount{0}; 39 | for (size_t i{0}; i < p_InputLen - s_Limit; i++) { 40 | if (p_Occurances != 0 && s_HitCount >= p_Occurances) { 41 | break; 42 | } 43 | if (p_Input[i] == p_Search[0]) { 44 | if (std::memcmp(&p_Input[i], p_Search, p_SearchLen) == 0) { 45 | VLOG(1) << "Found match for patching at: " << hex(i); 46 | Offset(p_Input, p_InputLen, p_Replace, p_ReplaceLen, i); 47 | s_HitCount++; 48 | 49 | // Skip what we just patched so we don't go locked here 50 | i += p_SearchLen; 51 | } 52 | } 53 | } 54 | 55 | return s_HitCount; 56 | } 57 | 58 | uint64_t PatternStartAt(unsigned char *p_Input, size_t p_InputLen, const char *p_Search, size_t p_SearchLen, const char *p_Replace, size_t p_ReplaceLen, size_t p_StartAddr, size_t p_Occurances) { 59 | if (p_InputLen == 0 || p_SearchLen == 0 || p_ReplaceLen == 0 || p_InputLen == p_StartAddr) { 60 | return 0; 61 | } 62 | if (p_InputLen < p_StartAddr) { 63 | LOG(ERROR) << "Patch must not overflow input"; 64 | return 0; 65 | } 66 | 67 | return Pattern(p_Input + p_StartAddr, p_InputLen - p_StartAddr, p_Search, p_SearchLen, p_Replace, p_ReplaceLen, p_Occurances); 68 | } 69 | 70 | uint64_t PatternEndAt(unsigned char *p_Input, size_t p_InputLen, const char *p_Search, size_t p_SearchLen, const char *p_Replace, size_t p_ReplaceLen, size_t p_EndAddr, size_t p_Occurances) { 71 | if (p_InputLen == 0 || p_SearchLen == 0 || p_ReplaceLen == 0) { 72 | return 0; 73 | } 74 | if (p_InputLen < p_EndAddr) { 75 | LOG(ERROR) << "Patch must not overflow input"; 76 | return 0; 77 | } 78 | 79 | return Pattern(p_Input, p_EndAddr, p_Search, p_SearchLen, p_Replace, p_ReplaceLen, p_Occurances); 80 | } 81 | 82 | uint64_t PatternBetween(unsigned char *p_Input, size_t p_InputLen, const char *p_Search, size_t p_SearchLen, const char *p_Replace, size_t p_ReplaceLen, size_t p_StartAddr, size_t p_EndAddr, size_t p_Occurances) { 83 | if (p_InputLen == 0 || p_SearchLen == 0 || p_ReplaceLen == 0) { 84 | return 0; 85 | } 86 | if (p_EndAddr < p_StartAddr) { 87 | LOG(ERROR) << "Patch end address is before start address"; 88 | return 0; 89 | } 90 | if (p_InputLen < p_StartAddr) { 91 | LOG(ERROR) << "Patch must not overflow input"; 92 | return 0; 93 | } 94 | size_t s_EndAddr{p_EndAddr}; 95 | if (p_InputLen < p_EndAddr) { 96 | LOG(WARNING) << "Patch must not overflow input, using end of ipnut as end address"; 97 | s_EndAddr = p_InputLen; 98 | } 99 | 100 | return Pattern(p_Input + p_StartAddr, s_EndAddr - p_StartAddr, p_Search, p_SearchLen, p_Replace, p_ReplaceLen, p_Occurances); 101 | } 102 | } // namespace patch 103 | -------------------------------------------------------------------------------- /src/main_eapk.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "cmake_vars_eapk.h" 11 | #include "common.h" 12 | #include "eap.h" 13 | #include "key_store.h" 14 | #include "patch.h" 15 | 16 | #include "banned.h" 17 | 18 | DEFINE_bool(decrypt, false, "Run in decryption mode"); 19 | DEFINE_bool(encrypt, false, "Run in encryption mode"); 20 | DEFINE_string(input, "eap_kernel", "Path of the EAP kernel file to load"); 21 | DEFINE_string(keys, "keys.json", "Path of the key file to load"); 22 | DEFINE_int32(keyset, -1, "Which keyset to use. Only used for encryption (\"0\", \"1\", \"2\", or \"3\")"); 23 | DEFINE_string(output, "eap_kernel.modified", "Path to save the output EAP kernel file"); 24 | 25 | int main(int argc, char *argv[]) { 26 | gflags::SetUsageMessage(g_AppDescription); 27 | gflags::SetVersionString(g_AppVersion); 28 | 29 | gflags::ParseCommandLineFlags(&argc, &argv, true); 30 | 31 | google::InitGoogleLogging(argv[0]); 32 | google::InstallFailureSignalHandler(); 33 | 34 | if (!InitializeKeyStore(FLAGS_keys)) { 35 | LOG(FATAL) << "Could not read key file: " << FLAGS_keys; 36 | return 1; 37 | } 38 | 39 | if (FLAGS_decrypt && FLAGS_encrypt) { 40 | LOG(FATAL) << "Cannot use both \"encrypt\" or \"decrypt\" flag at the same time"; 41 | return 1; 42 | } 43 | if (!FLAGS_decrypt && !FLAGS_encrypt) { 44 | LOG(FATAL) << "Must use \"encrypt\" or \"decrypt\" flag"; 45 | return 1; 46 | } 47 | bool s_Encrypt{false}; 48 | uint32_t s_EncryptionKeyset; 49 | if (FLAGS_encrypt) { 50 | s_Encrypt = true; 51 | if (FLAGS_keyset != 0 && FLAGS_keyset != 1 && FLAGS_keyset != 2 && FLAGS_keyset != 3) { 52 | LOG(FATAL) << "Invalid keyset: " << FLAGS_keyset << "\nMust be \"0\", \"1\", \"2\", or \"3\""; 53 | return 1; 54 | } 55 | s_EncryptionKeyset = FLAGS_keyset; 56 | } 57 | 58 | if (FLAGS_input == FLAGS_output) { 59 | LOG(FATAL) << "Input and output path cannot be the same"; 60 | return 1; 61 | } 62 | 63 | std::filesystem::path s_InputPath(FLAGS_input); 64 | std::filesystem::path s_OutputPath(FLAGS_output); 65 | if (FLAGS_input != "eap_kernel" && FLAGS_output == "eap_kernel.modified") { 66 | s_OutputPath = s_InputPath; 67 | s_OutputPath += ".modified"; 68 | } 69 | 70 | gflags::ShutDownCommandLineFlags(); 71 | 72 | LOG(INFO) << "Initalizing..."; 73 | 74 | std::ifstream s_InputFile(s_InputPath, std::ios::in | std::ios::binary); 75 | if (!s_InputFile || !s_InputFile.good()) { 76 | s_InputFile.close(); 77 | LOG(FATAL) << "Unable to open input file " << s_InputPath; 78 | return 1; 79 | } 80 | uint64_t s_InputLen{std::filesystem::file_size(s_InputPath)}; 81 | 82 | std::vector s_InputData(s_InputLen); 83 | if (!s_InputFile.read(reinterpret_cast(&s_InputData[0]), s_InputData.size()).good()) { // Flawfinder: ignore 84 | s_InputFile.close(); 85 | LOG(FATAL) << "Unable to read input file " << s_InputPath; 86 | return 1; 87 | } 88 | s_InputFile.close(); 89 | 90 | std::vector s_OutputData; 91 | if (s_Encrypt) { 92 | if (!eap::EncryptKernel(&s_InputData[0], s_InputLen, s_EncryptionKeyset, s_OutputData)) { 93 | LOG(FATAL) << "Failed to encrypt EAP kernel"; 94 | return 1; 95 | } 96 | } else { 97 | if (!eap::DecryptKernel(&s_InputData[0], s_InputLen, s_OutputData)) { 98 | LOG(FATAL) << "Failed to decrypt EAP kernel"; 99 | return 1; 100 | } 101 | } 102 | 103 | if (s_OutputData.size() == 0) { 104 | LOG(FATAL) << "Failed to modify EAP kernel"; 105 | return 1; 106 | } 107 | 108 | if (!WriteFile(&s_OutputData[0], s_OutputData.size(), s_OutputPath)) { 109 | LOG(FATAL) << "Failed to write output to file to " << s_OutputPath; 110 | return 1; 111 | } 112 | 113 | LOG(INFO) << "Done, file saved to " << s_OutputPath; 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /src/main_eap.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "cmake_vars_eap.h" 11 | #include "common.h" 12 | #include "eap.h" 13 | #include "key_store.h" 14 | #include "patch.h" 15 | 16 | #include "banned.h" 17 | 18 | DEFINE_bool(decrypt, false, "Run in decryption mode"); 19 | DEFINE_bool(encrypt, false, "Run in encryption mode"); 20 | DEFINE_string(input, "C0010001", "Path of the EAP KBL file to load"); 21 | DEFINE_string(keys, "keys.json", "Path of the key file to load"); 22 | DEFINE_string(output, "C0010001.modified", "Path to save the output EAP KBL file"); 23 | DEFINE_string(revision, "", "Which southbridge revision keyset to use. Only used for encryption (\"AEOLIA\", \"BELIZE\", \"BELIZE 2\", or \"BAIKAL\")"); 24 | 25 | int main(int argc, char *argv[]) { 26 | gflags::SetUsageMessage(g_AppDescription); 27 | gflags::SetVersionString(g_AppVersion); 28 | 29 | gflags::ParseCommandLineFlags(&argc, &argv, true); 30 | 31 | google::InitGoogleLogging(argv[0]); 32 | google::InstallFailureSignalHandler(); 33 | 34 | if (!InitializeKeyStore(FLAGS_keys)) { 35 | LOG(FATAL) << "Could not read key file: " << FLAGS_keys; 36 | return 1; 37 | } 38 | 39 | if (FLAGS_decrypt && FLAGS_encrypt) { 40 | LOG(FATAL) << "Cannot use both \"encrypt\" or \"decrypt\" flag at the same time"; 41 | return 1; 42 | } 43 | if (!FLAGS_decrypt && !FLAGS_encrypt) { 44 | LOG(FATAL) << "Must use \"encrypt\" or \"decrypt\" flag"; 45 | return 1; 46 | } 47 | bool s_Encrypt{false}; 48 | std::string s_SouthbridgeRevision; 49 | if (FLAGS_encrypt) { 50 | s_Encrypt = true; 51 | if (FLAGS_revision != "AEOLIA" && FLAGS_revision != "BELIZE" && FLAGS_revision != "BELIZE 2" && FLAGS_revision != "BAIKAL") { 52 | LOG(FATAL) << "Invalid southbridge revision: " << FLAGS_revision << "\nMust be \"AEOLIA\", \"BELIZE\", \"BELIZE 2\", or \"BAIKAL\""; 53 | return 1; 54 | } 55 | s_SouthbridgeRevision = FLAGS_revision; 56 | } 57 | 58 | if (FLAGS_input == FLAGS_output) { 59 | LOG(FATAL) << "Input and output path cannot be the same"; 60 | return 1; 61 | } 62 | 63 | std::filesystem::path s_InputPath(FLAGS_input); 64 | std::filesystem::path s_OutputPath(FLAGS_output); 65 | if (FLAGS_input != "C0010001" && FLAGS_output == "C0010001.modified") { 66 | s_OutputPath = s_InputPath; 67 | s_OutputPath += ".modified"; 68 | } 69 | 70 | gflags::ShutDownCommandLineFlags(); 71 | 72 | LOG(INFO) << "Initalizing..."; 73 | 74 | std::ifstream s_InputFile(s_InputPath, std::ios::in | std::ios::binary); 75 | if (!s_InputFile || !s_InputFile.good()) { 76 | s_InputFile.close(); 77 | LOG(FATAL) << "Unable to open input file " << s_InputPath; 78 | return 1; 79 | } 80 | uint64_t s_InputLen{std::filesystem::file_size(s_InputPath)}; 81 | 82 | std::vector s_InputData(s_InputLen); 83 | if (!s_InputFile.read(reinterpret_cast(&s_InputData[0]), s_InputData.size()).good()) { // Flawfinder: ignore 84 | s_InputFile.close(); 85 | LOG(FATAL) << "Unable to read input file " << s_InputPath; 86 | return 1; 87 | } 88 | s_InputFile.close(); 89 | 90 | std::vector s_OutputData; 91 | if (s_Encrypt) { 92 | if (!eap::EncryptKbl(&s_InputData[0], s_InputLen, s_SouthbridgeRevision, s_OutputData)) { 93 | LOG(FATAL) << "Failed to encrypt EAP KBL"; 94 | return 1; 95 | } 96 | } else { 97 | if (!eap::DecryptKbl(&s_InputData[0], s_InputLen, s_OutputData)) { 98 | LOG(FATAL) << "Failed to decrypt EAP KBL"; 99 | return 1; 100 | } 101 | } 102 | 103 | if (s_OutputData.size() == 0) { 104 | LOG(FATAL) << "Failed to modify EAP KBL"; 105 | return 1; 106 | } 107 | 108 | if (!WriteFile(&s_OutputData[0], s_OutputData.size(), s_OutputPath)) { 109 | LOG(FATAL) << "Failed to write output to file to " << s_OutputPath; 110 | return 1; 111 | } 112 | 113 | LOG(INFO) << "Done, file saved to " << s_OutputPath; 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /src/main_emc.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "cmake_vars_emc.h" 11 | #include "common.h" 12 | #include "emc.h" 13 | #include "key_store.h" 14 | #include "patch.h" 15 | 16 | #include "banned.h" 17 | 18 | DEFINE_bool(decrypt, false, "Run in decryption mode"); 19 | DEFINE_bool(encrypt, false, "Run in encryption mode"); 20 | DEFINE_bool(godmode, false, "Should \"Godmode\" patches be applied"); 21 | DEFINE_string(input, "C0000001", "Path of the EMC IPL file to load"); 22 | DEFINE_string(keys, "keys.json", "Path of the key file to load"); 23 | DEFINE_string(output, "C0000001.modified", "Path to save the output EAP KBL file"); 24 | DEFINE_string(revision, "", "Which southbridge revision keyset to use. Only used for encryption (\"AEOLIA\", \"BELIZE\", \"BELIZE 2\", or \"BAIKAL\")"); 25 | 26 | int main(int argc, char *argv[]) { 27 | gflags::SetUsageMessage(g_AppDescription); 28 | gflags::SetVersionString(g_AppVersion); 29 | 30 | gflags::ParseCommandLineFlags(&argc, &argv, true); 31 | 32 | google::InitGoogleLogging(argv[0]); 33 | google::InstallFailureSignalHandler(); 34 | 35 | if (!InitializeKeyStore(FLAGS_keys)) { 36 | LOG(FATAL) << "Could not read key file: " << FLAGS_keys; 37 | return 1; 38 | } 39 | 40 | if (FLAGS_decrypt && FLAGS_encrypt) { 41 | LOG(FATAL) << "Cannot use both \"encrypt\" or \"decrypt\" flag at the same time"; 42 | return 1; 43 | } 44 | if (!FLAGS_decrypt && !FLAGS_encrypt) { 45 | LOG(FATAL) << "Must use \"encrypt\" or \"decrypt\" flag"; 46 | return 1; 47 | } 48 | bool s_Encrypt{false}; 49 | std::string s_SouthbridgeRevision; 50 | if (FLAGS_encrypt) { 51 | s_Encrypt = true; 52 | if (FLAGS_revision != "AEOLIA" && FLAGS_revision != "BELIZE" && FLAGS_revision != "BELIZE 2" && FLAGS_revision != "BAIKAL") { 53 | LOG(FATAL) << "Invalid southbridge revision: " << FLAGS_revision << "\nMust be \"AEOLIA\", \"BELIZE\", \"BELIZE 2\", or \"BAIKAL\""; 54 | return 1; 55 | } 56 | s_SouthbridgeRevision = FLAGS_revision; 57 | } 58 | 59 | bool s_Godmode{FLAGS_godmode}; 60 | 61 | if (FLAGS_input == FLAGS_output) { 62 | LOG(FATAL) << "Input and output path cannot be the same"; 63 | return 1; 64 | } 65 | 66 | std::filesystem::path s_InputPath(FLAGS_input); 67 | std::filesystem::path s_OutputPath(FLAGS_output); 68 | if (FLAGS_input != "C0000001" && FLAGS_output == "C0000001.modified") { 69 | s_OutputPath = s_InputPath; 70 | s_OutputPath += ".modified"; 71 | } 72 | 73 | gflags::ShutDownCommandLineFlags(); 74 | 75 | LOG(INFO) << "Initalizing..."; 76 | 77 | std::ifstream s_InputFile(s_InputPath, std::ios::in | std::ios::binary); 78 | if (!s_InputFile || !s_InputFile.good()) { 79 | s_InputFile.close(); 80 | LOG(FATAL) << "Unable to open input file " << s_InputPath; 81 | return 1; 82 | } 83 | uint64_t s_InputLen{std::filesystem::file_size(s_InputPath)}; 84 | 85 | std::vector s_InputData(s_InputLen); 86 | if (!s_InputFile.read(reinterpret_cast(&s_InputData[0]), s_InputData.size()).good()) { // Flawfinder: ignore 87 | s_InputFile.close(); 88 | LOG(FATAL) << "Unable to read input file " << s_InputPath; 89 | return 1; 90 | } 91 | s_InputFile.close(); 92 | 93 | std::vector s_OutputData; 94 | if (s_Encrypt) { 95 | if (s_Godmode) { 96 | LOG(INFO) << "Applying \"Godmode\" patches..."; 97 | patch::Pattern(&s_InputData[0], s_InputData.size(), "\x03\x00\xFD\x00", 4, "\x0F", 1); 98 | patch::Pattern(&s_InputData[0], s_InputData.size(), "\x07\x00\xFD\x00", 4, "\x0F", 1); 99 | } 100 | if (!emc::Encrypt(&s_InputData[0], s_InputLen, s_SouthbridgeRevision, s_OutputData)) { 101 | LOG(FATAL) << "Failed to encrypt EMC IPL"; 102 | return 1; 103 | } 104 | } else { 105 | if (!emc::Decrypt(&s_InputData[0], s_InputLen, s_OutputData)) { 106 | LOG(FATAL) << "Failed to decrypt EMC IPL"; 107 | return 1; 108 | } 109 | if (s_Godmode) { 110 | LOG(INFO) << "Applying \"Godmode\" patches..."; 111 | patch::Pattern(&s_OutputData[0], s_OutputData.size(), "\x03\x00\xFD\x00", 4, "\x0F", 1); 112 | patch::Pattern(&s_OutputData[0], s_OutputData.size(), "\x07\x00\xFD\x00", 4, "\x0F", 1); 113 | } 114 | } 115 | 116 | if (s_OutputData.size() == 0) { 117 | LOG(FATAL) << "Failed to modify EMC IPL"; 118 | return 1; 119 | } 120 | 121 | if (!WriteFile(&s_OutputData[0], s_OutputData.size(), s_OutputPath)) { 122 | LOG(FATAL) << "Failed to write output to file to " << s_OutputPath; 123 | return 1; 124 | } 125 | 126 | LOG(INFO) << "Done, file saved to " << s_OutputPath; 127 | 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /src/common.cc: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "banned.h" 18 | 19 | bool CheckMagic(const unsigned char *p_Input, const char *p_Magic, size_t p_MagicLen) { 20 | if (p_Input == nullptr) { 21 | LOG(ERROR) << "Input data is NULL"; 22 | return false; 23 | } 24 | if (p_Magic == nullptr) { 25 | LOG(ERROR) << "Input magic is NULL"; 26 | return false; 27 | } 28 | if (p_MagicLen == 0) { 29 | LOG(ERROR) << "Magic length is zero"; 30 | return false; 31 | } 32 | 33 | // TODO: This does not work because one of the Magics we use has a null terminator in it which breaks strnlen 34 | // if (strnlen(reinterpret_cast(const_cast(p_Input)), p_MagicLen) < p_MagicLen || strnlen(p_Magic, p_MagicLen) < p_MagicLen) { 35 | // LOG(ERROR) << "Magic length is too large"; 36 | // return false; 37 | // } 38 | 39 | if (std::memcmp(p_Input, p_Magic, p_MagicLen) != 0) { 40 | LOG(ERROR) << "Magic does not match"; 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | // Checks if string is empty 48 | bool IsEmptyString(const std::string &p_InputString) { 49 | if (p_InputString.empty() || std::all_of(p_InputString.begin(), p_InputString.end(), [](char c) { return std::isspace(c); })) { 50 | return true; 51 | } 52 | 53 | return false; 54 | } 55 | 56 | bool WriteFile(const unsigned char *p_Input, size_t p_InputLen, const std::string &p_OutputPath, int64_t p_Offset) { 57 | if (p_Input == nullptr && p_InputLen != 0) { 58 | LOG(ERROR) << "Input data is NULL"; 59 | return false; 60 | } 61 | 62 | if (IsEmptyString(p_OutputPath)) { 63 | LOG(ERROR) << "Empty path argument"; 64 | return false; 65 | } 66 | 67 | // Zero byte files DO exist, but we can use this check to help find/debug issues 68 | if (p_InputLen == 0) { 69 | LOG(WARNING) << "Input length is zero"; 70 | } 71 | 72 | // mkdir -p 73 | std::filesystem::path s_RootDirectory(p_OutputPath); 74 | s_RootDirectory.remove_filename(); 75 | if (!IsEmptyString(s_RootDirectory.string())) { 76 | if (!std::filesystem::exists(s_RootDirectory)) { 77 | if (!std::filesystem::create_directories(s_RootDirectory)) { 78 | LOG(ERROR) << "Failed to create directory: " << s_RootDirectory; 79 | return false; 80 | } 81 | } 82 | } 83 | 84 | // Exists, but is not a file 85 | if (std::filesystem::exists(p_OutputPath) && !std::filesystem::is_regular_file(p_OutputPath)) { 86 | LOG(ERROR) << "Output path exists, but is not a file: " << p_OutputPath; 87 | return false; 88 | } 89 | 90 | // Write 91 | if (p_Offset == 0) { 92 | std::ofstream s_OutputFile(p_OutputPath, std::ios::out | std::ios::trunc | std::ios::binary); 93 | if (!s_OutputFile || !s_OutputFile.good()) { 94 | LOG(ERROR) << "Cannot open output file: " << p_OutputPath; 95 | s_OutputFile.close(); 96 | return false; 97 | } 98 | 99 | s_OutputFile.write(reinterpret_cast(const_cast(&p_Input[0])), p_InputLen); 100 | s_OutputFile.close(); 101 | } else { 102 | std::ofstream s_OutputFile(p_OutputPath, std::ios::out | std::ios::in | std::ios::binary); 103 | if (!s_OutputFile || !s_OutputFile.good()) { 104 | LOG(ERROR) << "Cannot open output file: " << p_OutputPath; 105 | s_OutputFile.close(); 106 | return false; 107 | } 108 | 109 | s_OutputFile.seekp(p_Offset, std::ios::beg); 110 | s_OutputFile.write(reinterpret_cast(const_cast(&p_Input[0])), p_InputLen); 111 | s_OutputFile.close(); 112 | } 113 | 114 | return true; 115 | } 116 | 117 | bool EndsWith(const std::string &p_Input, const std::string &p_Match) { 118 | if (IsEmptyString(p_Match) && !IsEmptyString(p_Input)) { 119 | return false; 120 | } 121 | 122 | if (p_Input.size() >= p_Match.size() && p_Input.compare(p_Input.size() - p_Match.size(), p_Match.size(), p_Match) == 0) { 123 | return true; 124 | } 125 | 126 | return false; 127 | } 128 | 129 | // Hexdump based on: https://stackoverflow.com/a/29865 130 | std::string HexDump(const void *p_Pointer, uint32_t p_Len) { 131 | const unsigned char *s_Buffer{static_cast(p_Pointer)}; 132 | 133 | std::stringstream s_OutputString; 134 | 135 | for (uint32_t i{0}; i < p_Len; i += 16) { 136 | s_OutputString << std::uppercase << std::setfill('0') << std::setw(8) << std::hex << i << ": "; 137 | for (uint32_t j{0}; j < 16; j++) { 138 | if (i + j < p_Len) { 139 | s_OutputString << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << static_cast(s_Buffer[i + j]) << " "; 140 | } else { 141 | s_OutputString << " "; 142 | } 143 | if (j == 7) { 144 | s_OutputString << " "; 145 | } 146 | } 147 | 148 | s_OutputString << " "; 149 | 150 | for (uint32_t j{0}; j < 16; j++) { 151 | if (i + j < p_Len) { 152 | if (isprint(s_Buffer[i + j])) { 153 | s_OutputString << s_Buffer[i + j]; 154 | } else { 155 | s_OutputString << "."; 156 | } 157 | } 158 | } 159 | 160 | if (i + 16 < p_Len) { 161 | s_OutputString << "\n"; 162 | } 163 | } 164 | 165 | return s_OutputString.str(); 166 | } 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # "CFW" Toolkit 2 | 3 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/b4c0b036b73d4547a05a604b726b51a3)](https://www.codacy.com/gh/Al-Azif/ps4-cfw-toolkit/dashboard) 4 | 5 | With the proper keys, all of which can be obtained from the console, you can decrypt AND properly encrypt the following binary images: 6 | 7 | - EAP KBL (Kernel Boot Loader) 8 | - EAP Kernel 9 | - EMC IPL (Initial Program Load) 10 | - Syscon (Both Patch and Full) 11 | 12 | What's missing as far as custom code running EVERYWHERE, that's not currently supported within this repo: 13 | 14 | - SAMU IPL (Encrypted with PCKs within Sflash and signed with private keys) 15 | - Required for PS3 style CFW where you just install a PUP 16 | - Private keys are NOT on the console 17 | - Seven revisions 18 | - SELF Files (Encrypted and signed with private keys) 19 | - Would not matter if SAMU IPL is broken/custom 20 | - Private keys are NOT on the console 21 | - Bluetooth/WiFi FW (Not encrypted or signed. One of them is packed, it's just a ZIP) 22 | - Three revisions 23 | - BD Drive FW (Haven't looked at it) 24 | - Six revisions 25 | - USB SATA Bridge FW (Haven't looked at it) 26 | - One revision 27 | - Communication Processor FW (Haven't looked at it) 28 | - Devkit only 29 | - One revision 30 | 31 | ## Requirements 32 | 33 | - C++ Compiler (Clang >= 9.00 Recommended) 34 | - CMake >=3.10.2 35 | - [gflags](https://github.com/gflags/gflags) 36 | - [glog](https://github.com/google/glog) 37 | - OpenSSL (1.1.1 Recommended) 38 | - \>=3.0.0 will raise warnings for depreciated low level API usage. With the included C++ flags warning are errors. 39 | 40 | ## EAP 41 | 42 | ### Synopsis 43 | 44 | Decrypts/Encrypts EAP KBL (Kernel Boot Loader) images. Located at /dev/sflash0s0x33 45 | 46 | ### Usage 47 | 48 | Flags 49 | > -decrypt (Run in decryption mode) type: bool default: false
50 | > -encrypt (Run in encryption mode) type: bool default: false
51 | > -input (Path of the EAP KBL file to load) type: string default: "C0010001"
52 | > -keys (Path of the key file to load) type: string default: "keys.json"
53 | > -output (Path to save the output EAP KBL file) type: string default: "C0010001.modified"
54 | > -revision (Which southbridge revision keyset to use. Only used for encryption ("AEOLIA", "BELIZE", "BELIZE 2", or "BAIKAL")) type: string default: "" 55 | 56 | ## EAPK 57 | 58 | ### Synopsis 59 | 60 | Decrypts/Encrypts EAP Kernel images. Located at /dev/da0x2 61 | 62 | ### Usage 63 | 64 | Flags 65 | > -decrypt (Run in decryption mode) type: bool default: false
66 | > -encrypt (Run in encryption mode) type: bool default: false
67 | > -input (Path of the EAP kernel file to load) type: string default: "eap_kernel"
68 | > -keys (Path of the key file to load) type: string default: "keys.json"
69 | > -keyset (Which keyset to use. Only used for encryption ("0", "1", "2", or "3")) type: int32 default: -1
70 | > -output (Path to save the output EAP kernel file) type: string default: "eap_kernel.modified" 71 | 72 | ## EMC 73 | 74 | ### Synopsis 75 | 76 | Decrypts/Encrypts EMC images. Can apply "Godmode" patches during either operation. Located at /dev/sflash0s0x32b 77 | 78 | ### Usage 79 | 80 | Flags 81 | > -decrypt (Run in decryption mode) type: bool default: false
82 | > -encrypt (Run in encryption mode) type: bool default: false
83 | > -godmode (Should "Godmode" patches be applied) type: bool default: false
84 | > -input (Path of the EMC IPL file to load) type: string default: "C0000001"
85 | > -keys (Path of the key file to load) type: string default: "keys.json"
86 | > -output (Path to save the output EAP KBL file) type: string default: "C0000001.modified"
87 | > -revision (Which southbridge revision keyset to use. Only used for encryption ("AEOLIA", "BELIZE", "BELIZE 2", or "BAIKAL")) type: string default: "" 88 | 89 | ## Syscon 90 | 91 | ### Synopsis 92 | 93 | Decrypts/Encrypts SYSCON images. Inaccessible from filsystem 94 | 95 | ### Usage 96 | 97 | Flags 98 | > -decrypt (Run in decryption mode) type: bool default: false
99 | > -encrypt (Run in encryption mode) type: bool default: false
100 | > -input (Path of the SYSCON file to load) type: string default: "40000001"
101 | > -keys (Path of the key file to load) type: string default: "keys.json"
102 | > -output (Path to save the output SYSCON file) type: string default: "40000001.modified" 103 | 104 | ## Notes 105 | 106 | - This is tested on WSL with Clang 10 and OpenSSL 1.1.1, support for anything else is not guaranteed. 107 | - Output binary files will be located in `bin/` 108 | - The input binary for encryption doesn't do any checks beyond checking to see if the file magic is correct and the file size will fit the free space available when installed. It's your responsibility to make sure the binary you're feeding it is built correctly/valid. 109 | - This is stripped out of a larger project so somethings may not make since/be optimal in this context, however it should function as expected. I did my best minimizing it without rewriting any of it. Any major changes in functionality may not work within the context of the larger program (Or already be done) so send me a DM before starting to work on major changes. 110 | - Some features were removed to not step on toes and will likely be added at a later date. No ETA. 111 | - Keys not included, check the dev wiki or something. This is on purpose... do not submit them please, update the wiki if you're adding new ones. 112 | - Be sure to have a way to restore from a backup if you are using real hardware 113 | -------------------------------------------------------------------------------- /src/syscon.cc: -------------------------------------------------------------------------------- 1 | #include "syscon.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "common.h" 11 | #include "crypto.h" 12 | #include "key_store.h" 13 | 14 | #include "banned.h" 15 | 16 | namespace syscon { 17 | static bool DecryptCommon(unsigned char *p_AesKey, size_t p_AesLen, unsigned char *p_Iv, unsigned char *p_MacKey, size_t p_MacLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 18 | VLOG(3) << "AES_KEY:\n" << HexDump(p_AesKey, p_AesLen); 19 | VLOG(3) << "IV:\n" << HexDump(p_Iv, 0x10); 20 | VLOG(3) << "MAC_KEY:\n" << HexDump(p_MacKey, p_MacLen); 21 | 22 | p_Output.clear(); 23 | p_Output.shrink_to_fit(); 24 | 25 | if (AesDecryptCbc(p_AesKey, p_AesLen, p_Iv, p_Input, p_InputLen, p_Output) != p_InputLen) { 26 | LOG(ERROR) << "Error decrypting data"; 27 | p_Output.clear(); 28 | p_Output.shrink_to_fit(); 29 | return false; 30 | } 31 | 32 | // The static value `0x10` below is the MAC length per rfc4493 33 | std::vector s_CalculatedCmac; 34 | if (!Cmac(p_MacKey, p_MacLen, &p_Output[0x10], p_Output.size() - 0x10, s_CalculatedCmac)) { 35 | LOG(WARNING) << "Error calculating CMAC digest"; 36 | return false; 37 | } 38 | 39 | if (std::memcmp(&p_Output[0], &s_CalculatedCmac[0], s_CalculatedCmac.size()) != 0) { 40 | LOG(WARNING) << "CMAC digest does not match"; 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | static bool DecryptPatch(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 48 | GetKeyToVariable("SYSCON", "PATCH_AES_KEY", s_AesKey); 49 | GetKeyToVariable("SYSCON", "PATCH_IV", s_Iv); 50 | GetKeyToVariable("SYSCON", "PATCH_MAC_KEY", s_MacKey); 51 | 52 | return DecryptCommon(s_AesKey, GetKeySize("SYSCON", "PATCH_AES_KEY"), s_Iv, s_MacKey, GetKeySize("SYSCON", "PATCH_MAC_KEY"), p_Input, p_InputLen, p_Output); 53 | } 54 | 55 | static bool DecryptFull(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 56 | GetKeyToVariable("SYSCON", "FULL_AES_KEY", s_AesKey); 57 | GetKeyToVariable("SYSCON", "FULL_IV", s_Iv); 58 | GetKeyToVariable("SYSCON", "FULL_MAC_KEY", s_MacKey); 59 | 60 | return DecryptCommon(s_AesKey, GetKeySize("SYSCON", "FULL_AES_KEY"), s_Iv, s_MacKey, GetKeySize("SYSCON", "FULL_MAC_KEY"), p_Input, p_InputLen, p_Output); 61 | } 62 | 63 | bool Decrypt(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 64 | if (p_Input == &p_Output[0]) { 65 | LOG(ERROR) << "Input is at the same location as output"; 66 | return false; 67 | } 68 | 69 | p_Output.clear(); 70 | p_Output.shrink_to_fit(); 71 | 72 | if (p_Input == nullptr) { 73 | LOG(ERROR) << "Input data is NULL"; 74 | return false; 75 | } 76 | if (p_InputLen == 0) { 77 | LOG(ERROR) << "Input length is zero"; 78 | return false; 79 | } 80 | 81 | if (!DecryptPatch(p_Input, p_InputLen, p_Output)) { 82 | LOG(WARNING) << "Input data is not a \"patch\" SYSCON image, attemping to decrypt as a \"full\" SYSCON image"; 83 | if (!DecryptFull(p_Input, p_InputLen, p_Output)) { 84 | LOG(ERROR) << "Input data is not a SYSCON image"; 85 | return false; 86 | } 87 | } 88 | 89 | return true; 90 | } 91 | 92 | static bool EncryptCommon(unsigned char *p_AesKey, size_t p_AesLen, unsigned char *p_Iv, unsigned char *p_MacKey, size_t p_MacLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 93 | VLOG(3) << "AES_KEY:\n" << HexDump(p_AesKey, p_AesLen); 94 | VLOG(3) << "IV:\n" << HexDump(p_Iv, 0x10); 95 | VLOG(3) << "MAC_KEY:\n" << HexDump(p_MacKey, p_MacLen); 96 | 97 | p_Output.clear(); 98 | p_Output.shrink_to_fit(); 99 | 100 | std::vector s_CompiledInput; 101 | Cmac(p_MacKey, p_MacLen, &p_Input[0], p_InputLen, s_CompiledInput); 102 | 103 | s_CompiledInput.insert(s_CompiledInput.begin(), &p_Input[0], &p_Input[p_InputLen]); 104 | 105 | if (AesEncryptCbc(p_AesKey, p_AesLen, p_Iv, &s_CompiledInput[0], s_CompiledInput.size(), p_Output) != p_InputLen) { 106 | LOG(ERROR) << "Error encrypting data"; 107 | p_Output.clear(); 108 | p_Output.shrink_to_fit(); 109 | return false; 110 | } 111 | 112 | return true; 113 | } 114 | 115 | static bool EncryptPatch(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 116 | GetKeyToVariable("SYSCON", "PATCH_AES_KEY", s_AesKey); 117 | GetKeyToVariable("SYSCON", "PATCH_IV", s_Iv); 118 | GetKeyToVariable("SYSCON", "PATCH_MAC_KEY", s_MacKey); 119 | 120 | return EncryptCommon(s_AesKey, GetKeySize("SYSCON", "PATCH_AES_KEY"), s_Iv, s_MacKey, GetKeySize("SYSCON", "PATCH_MAC_KEY"), p_Input, p_InputLen, p_Output); 121 | } 122 | 123 | static bool EncryptFull(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 124 | GetKeyToVariable("SYSCON", "FULL_AES_KEY", s_AesKey); 125 | GetKeyToVariable("SYSCON", "FULL_IV", s_Iv); 126 | GetKeyToVariable("SYSCON", "FULL_MAC_KEY", s_MacKey); 127 | 128 | return EncryptCommon(s_AesKey, GetKeySize("SYSCON", "FULL_AES_KEY"), s_Iv, s_MacKey, GetKeySize("SYSCON", "FULL_MAC_KEY"), p_Input, p_InputLen, p_Output); 129 | } 130 | 131 | bool Encrypt(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 132 | if (p_Input == &p_Output[0]) { 133 | LOG(ERROR) << "Input is at the same location as output"; 134 | return false; 135 | } 136 | 137 | p_Output.clear(); 138 | p_Output.shrink_to_fit(); 139 | 140 | if (p_Input == nullptr) { 141 | LOG(ERROR) << "Input data is NULL"; 142 | return false; 143 | } 144 | if (p_InputLen == 0) { 145 | LOG(ERROR) << "Input length is zero"; 146 | return false; 147 | } 148 | 149 | bool s_Patch = false; 150 | uint32_t s_CmacLen = 0; // The static value `0x10` that can be assigned below is the MAC length per rfc4493 151 | 152 | if (CheckMagic(&p_Input[0], c_SysconBlnkMagic_, sizeof(c_SysconBlnkMagic_)) || // 153 | CheckMagic(&p_Input[0], c_SysconBaseMagic_, sizeof(c_SysconBaseMagic_)) || // 154 | CheckMagic(&p_Input[0], c_SysconSystMagic_, sizeof(c_SysconSystMagic_))) { 155 | s_Patch = false; 156 | s_CmacLen = 0; 157 | } else if (CheckMagic(&p_Input[0x10], c_SysconBlnkMagic_, sizeof(c_SysconBlnkMagic_)) || // 158 | CheckMagic(&p_Input[0x10], c_SysconBaseMagic_, sizeof(c_SysconBaseMagic_)) || // 159 | CheckMagic(&p_Input[0x10], c_SysconSystMagic_, sizeof(c_SysconSystMagic_))) { 160 | s_Patch = false; 161 | s_CmacLen = 0x10; 162 | } else if (CheckMagic(&p_Input[0], c_SysconPtchMagic_, sizeof(c_SysconPtchMagic_))) { 163 | s_Patch = true; 164 | s_CmacLen = 0; 165 | } else if (CheckMagic(&p_Input[0x10], c_SysconPtchMagic_, sizeof(c_SysconPtchMagic_))) { 166 | s_Patch = true; 167 | s_CmacLen = 0x10; 168 | } else { 169 | LOG(ERROR) << "Input data is not a SYSCON image"; 170 | return false; 171 | } 172 | 173 | if (s_Patch) { 174 | if (p_InputLen > 0x7F0 + s_CmacLen) { // Max size is 0x800 with CMAC 175 | LOG(ERROR) << "Input length is invalid"; 176 | return false; 177 | } 178 | return EncryptPatch(p_Input + s_CmacLen, p_InputLen - s_CmacLen, p_Output); 179 | } 180 | 181 | if (p_InputLen > UINT32_MAX) { // TODO: Check for ACTUAL valid max length 182 | LOG(ERROR) << "Input length is invalid"; 183 | return false; 184 | } 185 | return EncryptFull(p_Input + s_CmacLen, p_InputLen - s_CmacLen, p_Output); 186 | } 187 | } // namespace syscon 188 | -------------------------------------------------------------------------------- /src/emc.cc: -------------------------------------------------------------------------------- 1 | #include "emc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "common.h" 15 | #include "crypto.h" 16 | #include "key_store.h" 17 | 18 | #include "banned.h" 19 | 20 | namespace emc { 21 | bool IsEmcIpl(const unsigned char *p_Input, size_t p_InputLen) { 22 | if (p_InputLen < sizeof(c_EmcMagic_)) { 23 | LOG(ERROR) << "Invalid size"; 24 | return false; 25 | } 26 | 27 | if (!CheckMagic(p_Input, c_EmcMagic_, sizeof(c_EmcMagic_))) { 28 | LOG(ERROR) << "Invalid magic"; 29 | return false; 30 | } 31 | 32 | if (p_InputLen <= sizeof(EmcIplHeader)) { 33 | LOG(ERROR) << "Invalid size"; 34 | return false; 35 | } 36 | 37 | EmcIplHeader *s_Header{reinterpret_cast(const_cast(p_Input))}; 38 | 39 | if (p_InputLen != sizeof(EmcIplHeader) + s_Header->body_size) { 40 | LOG(ERROR) << "Invalid size"; 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | bool Decrypt(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 48 | if (p_Input == &p_Output[0]) { 49 | LOG(ERROR) << "Input is at the same location as output"; 50 | return false; 51 | } 52 | 53 | p_Output.clear(); 54 | p_Output.shrink_to_fit(); 55 | 56 | if (p_Input == nullptr) { 57 | LOG(ERROR) << "Input data is NULL"; 58 | return false; 59 | } 60 | if (p_InputLen == 0) { 61 | LOG(ERROR) << "Input length is zero"; 62 | return false; 63 | } 64 | 65 | if (!IsEmcIpl(p_Input, p_InputLen)) { 66 | LOG(ERROR) << "File is not EMC IPL"; 67 | return false; 68 | } 69 | 70 | EmcIplHeader *s_Header{reinterpret_cast(const_cast(p_Input))}; 71 | 72 | VLOG(1) << "s_Header->version:\n" << HexDump(&s_Header->version, sizeof(s_Header->version)); 73 | VLOG(1) << "s_Header->type:\n" << HexDump(&s_Header->type, sizeof(s_Header->type)); 74 | VLOG(1) << "s_Header->header_size:\n" << HexDump(&s_Header->header_size, sizeof(s_Header->header_size)); 75 | VLOG(1) << "s_Header->body_size:\n" << HexDump(&s_Header->body_size, sizeof(s_Header->body_size)); 76 | VLOG(1) << "s_Header->entry_point:\n" << HexDump(&s_Header->entry_point, sizeof(s_Header->entry_point)); 77 | VLOG(1) << "s_Header->base_address:\n" << HexDump(&s_Header->base_address, sizeof(s_Header->base_address)); 78 | VLOG(1) << "s_Header->fill_pattern:\n" << HexDump(&s_Header->fill_pattern, sizeof(s_Header->fill_pattern)); 79 | VLOG(1) << "s_Header->key_seed:\n" << HexDump(&s_Header->key_seed, sizeof(s_Header->key_seed)); 80 | 81 | // TODO: Determine if the correct keyset can be deduced without brute forcing 82 | std::vector s_SouthbridgeRevisions{"AEOLIA", "BELIZE", "BELIZE 2", "BAIKAL"}; 83 | for (const std::string &l_Revision : s_SouthbridgeRevisions) { 84 | // Create copy of s_Header to use in loop 85 | EmcIplHeader l_Header = *s_Header; 86 | 87 | VLOG(1) << "Testing keys for " << l_Revision; 88 | 89 | GetKeyToVariable("EMC", "IPL_AES_KEY", l_Revision, s_AesKey); 90 | GetKeyToVariable("EMC", "IPL_IV", l_Revision, s_Iv); 91 | GetKeyToVariable("EMC", "IPL_MAC_KEY", l_Revision, s_MacKey); 92 | 93 | VLOG(3) << "EMC_IPL_AES_KEY:\n" << HexDump(s_AesKey, sizeof(s_AesKey)); 94 | VLOG(3) << "EMC_IPL_IV:\n" << HexDump(s_Iv, sizeof(s_Iv)); 95 | VLOG(3) << "EMC_IPL_MAC_KEY:\n" << HexDump(s_MacKey, sizeof(s_MacKey)); 96 | 97 | std::vector s_EncryptionHeader; 98 | if (AesDecryptCbc(s_AesKey, sizeof(s_AesKey), s_Iv, reinterpret_cast(s_Header) + sizeof(EmcIplHeader) - sizeof(EmcIplEncryptionHeader), sizeof(EmcIplEncryptionHeader), s_EncryptionHeader) != sizeof(EmcIplEncryptionHeader)) { 99 | LOG(ERROR) << "Error decrypting data with " << l_Revision << " keyset"; 100 | continue; 101 | } 102 | std::copy(s_EncryptionHeader.begin(), s_EncryptionHeader.end(), reinterpret_cast(&l_Header) + sizeof(EmcIplHeader) - s_EncryptionHeader.size()); 103 | 104 | VLOG(1) << "s_Header->body_aes_key:\n" << HexDump(&l_Header.body_aes_key, sizeof(l_Header.body_aes_key)); 105 | VLOG(1) << "s_Header->body_hmac_key:\n" << HexDump(&l_Header.body_hmac_key, sizeof(l_Header.body_hmac_key)); 106 | VLOG(1) << "s_Header->body_hmac:\n" << HexDump(&l_Header.body_hmac, sizeof(l_Header.body_hmac)); 107 | VLOG(1) << "s_Header->header_hmac:\n" << HexDump(&l_Header.header_hmac, sizeof(l_Header.header_hmac)); 108 | 109 | std::vector s_HeaderHmacReal; 110 | if (!HmacSha1(s_MacKey, sizeof(s_MacKey), reinterpret_cast(&l_Header), sizeof(EmcIplHeader) - sizeof(l_Header.header_hmac), s_HeaderHmacReal)) { 111 | LOG(WARNING) << "Error calculating header HMAC digest with " << l_Revision << " keyset"; 112 | continue; 113 | } 114 | VLOG(2) << "s_HeaderHmacReal:\n" << HexDump(&s_HeaderHmacReal[0], s_HeaderHmacReal.size()); 115 | 116 | if (std::memcmp(l_Header.header_hmac, &s_HeaderHmacReal[0], sizeof(l_Header.header_hmac)) != 0) { 117 | LOG(WARNING) << "Header HMAC does not match with " << l_Revision << " keyset"; 118 | continue; 119 | } 120 | 121 | if (AesDecryptCbc(l_Header.body_aes_key, sizeof(l_Header.body_aes_key), s_Iv, p_Input + sizeof(EmcIplHeader), l_Header.body_size, p_Output) != l_Header.body_size) { 122 | LOG(ERROR) << "Error decrypting data with " << l_Revision << " keyset"; 123 | p_Output.clear(); 124 | p_Output.shrink_to_fit(); 125 | continue; 126 | } 127 | 128 | std::vector s_BodyHmacReal; 129 | if (!HmacSha1(l_Header.body_hmac_key, sizeof(l_Header.body_hmac_key), p_Input + sizeof(EmcIplHeader), l_Header.body_size, s_BodyHmacReal)) { 130 | LOG(WARNING) << "Error calculating body HMAC digest with " << l_Revision << " keyset"; 131 | continue; 132 | } 133 | VLOG(2) << "s_BodyHmacReal:\n" << HexDump(&s_BodyHmacReal[0], s_BodyHmacReal.size()); 134 | 135 | if (std::memcmp(l_Header.body_hmac, &s_BodyHmacReal[0], sizeof(l_Header.body_hmac)) != 0) { 136 | LOG(WARNING) << "Body HMAC does not match with " << l_Revision << " keyset"; 137 | continue; 138 | } 139 | 140 | LOG(INFO) << "Southbridge keyset is " << l_Revision; 141 | return true; 142 | } 143 | 144 | LOG(ERROR) << "Could not find correct Southbridge keyset"; 145 | return false; 146 | } 147 | 148 | bool Encrypt(const unsigned char *p_Input, size_t p_InputLen, std::string p_SouthbridgeRevision, std::vector &p_Output) { 149 | if (p_Input == &p_Output[0]) { 150 | LOG(ERROR) << "Input is at the same location as output"; 151 | return false; 152 | } 153 | 154 | p_Output.clear(); 155 | p_Output.shrink_to_fit(); 156 | 157 | if (p_Input == nullptr) { 158 | LOG(ERROR) << "Input data is NULL"; 159 | return false; 160 | } 161 | if (p_InputLen == 0 || p_InputLen > 0x5FF94) { // Max size is 0x60000, packing adds an additional 0x6C bytes 162 | LOG(ERROR) << "Input length is invalid"; 163 | return false; 164 | } 165 | if (p_SouthbridgeRevision != "AEOLIA" && p_SouthbridgeRevision != "BELIZE" && p_SouthbridgeRevision != "BELIZE 2" && p_SouthbridgeRevision != "BAIKAL") { 166 | LOG(ERROR) << "Invalid southbridge revision"; 167 | return false; 168 | } 169 | 170 | // TODO: Check to see if input is a EMC IPL (No file magic number?) 171 | 172 | unsigned char s_BodyKey[AES_BLOCK_SIZE]; // Flawfinder: ignore 173 | if (!RAND_bytes(s_BodyKey, AES_BLOCK_SIZE)) { 174 | LOG(ERROR) << "Unable to generate new body AES key"; 175 | return false; 176 | } 177 | 178 | unsigned char s_BodyIv[AES_BLOCK_SIZE]; // Flawfinder: ignore 179 | std::memset(s_BodyIv, '\0', AES_BLOCK_SIZE); 180 | 181 | unsigned char s_BodyHmacKey[AES_BLOCK_SIZE]; // Flawfinder: ignore 182 | if (!RAND_bytes(s_BodyHmacKey, AES_BLOCK_SIZE)) { 183 | LOG(ERROR) << "Unable to generate new body MAC key"; 184 | return false; 185 | } 186 | 187 | std::vector s_EncryptedBody; 188 | if (AesEncryptCbc(s_BodyKey, AES_BLOCK_SIZE, s_BodyIv, p_Input, p_InputLen, s_EncryptedBody) != p_InputLen) { 189 | LOG(ERROR) << "Error decrypting data"; 190 | return false; 191 | } 192 | 193 | std::vector s_BodyHmac; 194 | if (!HmacSha1(s_BodyHmacKey, AES_BLOCK_SIZE, &s_EncryptedBody[0], s_EncryptedBody.size(), s_BodyHmac)) { 195 | LOG(ERROR) << "Error calculating body HMAC digest"; 196 | return false; 197 | } 198 | 199 | EmcIplHeader s_Header; 200 | std::copy(c_EmcMagic_, c_EmcMagic_ + sizeof(s_Header.magic), s_Header.magic); 201 | s_Header.version = 1; 202 | s_Header.type = 0x4801; // TODO: Is this calculated by version/item, or static 203 | s_Header.entry_point = 1051648; // TODO: Is this calculated by version/item, or static 204 | s_Header.base_address = 1051648; // TODO: Is this calculated by version/item, or static 205 | s_Header.body_size = static_cast(p_InputLen); // An oversized size_t is indirectly checked for earlier in this function 206 | s_Header.header_size = sizeof(EmcIplHeader); 207 | std::copy(c_FillPattern_, c_FillPattern_ + sizeof(s_Header.fill_pattern), s_Header.fill_pattern); // TODO: Is this calculated by version/item, or static 208 | std::copy(c_KeySeed_, c_KeySeed_ + sizeof(s_Header.key_seed), s_Header.key_seed); // TODO: Is this calculated by version/item, or static 209 | 210 | std::copy(s_BodyKey, s_BodyKey + sizeof(s_Header.body_aes_key), s_Header.body_aes_key); 211 | std::copy(s_BodyHmacKey, s_BodyHmacKey + sizeof(s_Header.body_hmac_key), s_Header.body_hmac_key); 212 | std::copy(s_BodyHmac.begin(), s_BodyHmac.begin() + sizeof(s_Header.body_hmac), s_Header.body_hmac); 213 | 214 | GetKeyToVariable("EMC", "IPL_AES_KEY", p_SouthbridgeRevision, s_AesKey); 215 | GetKeyToVariable("EMC", "IPL_IV", p_SouthbridgeRevision, s_Iv); 216 | GetKeyToVariable("EMC", "IPL_MAC_KEY", p_SouthbridgeRevision, s_MacKey); 217 | 218 | VLOG(3) << "EMC_IPL_AES_KEY:\n" << HexDump(s_AesKey, sizeof(s_AesKey)); 219 | VLOG(3) << "EMC_IPL_IV:\n" << HexDump(s_Iv, sizeof(s_Iv)); 220 | VLOG(3) << "EMC_IPL_MAC_KEY:\n" << HexDump(s_MacKey, sizeof(s_MacKey)); 221 | 222 | std::vector s_HeaderHmac; 223 | if (!HmacSha1(s_MacKey, sizeof(s_MacKey), reinterpret_cast(&s_Header), sizeof(EmcIplHeader) - sizeof(s_Header.header_hmac), s_HeaderHmac)) { 224 | LOG(ERROR) << "Error calculating header HMAC digest with " << p_SouthbridgeRevision << " keyset"; 225 | return false; 226 | } 227 | 228 | std::copy(s_HeaderHmac.begin(), s_HeaderHmac.begin() + sizeof(s_Header.header_hmac), s_Header.header_hmac); 229 | 230 | VLOG(1) << "s_Header->version:\n" << HexDump(&s_Header.version, sizeof(s_Header.version)); 231 | VLOG(1) << "s_Header->type:\n" << HexDump(&s_Header.type, sizeof(s_Header.type)); 232 | VLOG(1) << "s_Header->header_size:\n" << HexDump(&s_Header.header_size, sizeof(s_Header.header_size)); 233 | VLOG(1) << "s_Header->body_size:\n" << HexDump(&s_Header.body_size, sizeof(s_Header.body_size)); 234 | VLOG(1) << "s_Header->entry_point:\n" << HexDump(&s_Header.entry_point, sizeof(s_Header.entry_point)); 235 | VLOG(1) << "s_Header->base_address:\n" << HexDump(&s_Header.base_address, sizeof(s_Header.base_address)); 236 | VLOG(1) << "s_Header->fill_pattern:\n" << HexDump(&s_Header.fill_pattern, sizeof(s_Header.fill_pattern)); 237 | VLOG(1) << "s_Header->key_seed:\n" << HexDump(&s_Header.key_seed, sizeof(s_Header.key_seed)); 238 | VLOG(1) << "s_Header->body_aes_key:\n" << HexDump(&s_Header.body_aes_key, sizeof(s_Header.body_aes_key)); 239 | VLOG(1) << "s_Header->body_hmac_key:\n" << HexDump(&s_Header.body_hmac_key, sizeof(s_Header.body_hmac_key)); 240 | VLOG(1) << "s_Header->body_hmac:\n" << HexDump(&s_Header.body_hmac, sizeof(s_Header.body_hmac)); 241 | VLOG(1) << "s_Header->header_hmac:\n" << HexDump(&s_Header.header_hmac, sizeof(s_Header.header_hmac)); 242 | 243 | std::vector s_EncryptedHeader; 244 | if (AesEncryptCbc(s_AesKey, sizeof(s_AesKey), s_Iv, reinterpret_cast(&s_Header) + sizeof(EmcIplHeader) - sizeof(EmcIplEncryptionHeader), sizeof(EmcIplEncryptionHeader), s_EncryptedHeader) != sizeof(EmcIplEncryptionHeader)) { 245 | LOG(ERROR) << "Error encrypting data"; 246 | return false; 247 | } 248 | std::copy(s_EncryptedHeader.begin(), s_EncryptedHeader.end(), reinterpret_cast(&s_Header) + sizeof(EmcIplHeader) - s_EncryptedHeader.size()); 249 | 250 | p_Output.insert(p_Output.end(), reinterpret_cast(&s_Header), reinterpret_cast(&s_Header) + sizeof(s_Header)); 251 | p_Output.insert(p_Output.end(), s_EncryptedBody.begin(), s_EncryptedBody.end()); 252 | 253 | return true; 254 | } 255 | } // namespace emc 256 | -------------------------------------------------------------------------------- /src/eap.cc: -------------------------------------------------------------------------------- 1 | #include "eap.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "common.h" 15 | #include "crypto.h" 16 | #include "key_store.h" 17 | 18 | #include "banned.h" 19 | 20 | namespace eap { 21 | bool IsEapKbl(const unsigned char *p_Input, size_t p_InputLen) { 22 | if (p_InputLen < sizeof(c_EapKblMagic_)) { 23 | LOG(ERROR) << "Invalid size"; 24 | return false; 25 | } 26 | 27 | if (!CheckMagic(p_Input, c_EapKblMagic_, sizeof(c_EapKblMagic_))) { 28 | LOG(ERROR) << "Invalid magic"; 29 | return false; 30 | } 31 | 32 | if (p_InputLen < sizeof(EapKblHeader)) { 33 | LOG(ERROR) << "Invalid size"; 34 | return false; 35 | } 36 | 37 | EapKblHeader *s_Header{reinterpret_cast(const_cast(p_Input))}; 38 | 39 | if (p_InputLen != sizeof(EapKblHeader) + s_Header->body_size) { 40 | LOG(ERROR) << "Invalid size"; 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | bool IsEapKernel(const unsigned char *p_Input, size_t p_InputLen) { 48 | if (p_InputLen < sizeof(c_EapKernelMagic_)) { 49 | LOG(ERROR) << "Invalid size"; 50 | return false; 51 | } 52 | 53 | if (!CheckMagic(p_Input, c_EapKernelMagic_, sizeof(c_EapKernelMagic_))) { 54 | LOG(ERROR) << "Invalid magic"; 55 | return false; 56 | } 57 | 58 | if (p_InputLen < sizeof(EapKernelHeader)) { 59 | LOG(ERROR) << "Invalid size"; 60 | return false; 61 | } 62 | 63 | EapKernelHeader *s_StorageHeader{reinterpret_cast(const_cast(p_Input))}; 64 | 65 | if (s_StorageHeader->version != c_ExpectedVersion_) { 66 | LOG(ERROR) << "Invalid version"; 67 | return false; 68 | } 69 | 70 | // Can't calculate total size without decrypting first, so we'll end this here as it's out of scope of the function 71 | 72 | return true; 73 | } 74 | 75 | bool DecryptKbl(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 76 | if (p_Input == &p_Output[0]) { 77 | LOG(ERROR) << "Input is at the same location as output"; 78 | return false; 79 | } 80 | 81 | p_Output.clear(); 82 | p_Output.shrink_to_fit(); 83 | 84 | if (p_Input == nullptr) { 85 | LOG(ERROR) << "Input data is NULL"; 86 | return false; 87 | } 88 | if (p_InputLen == 0) { 89 | LOG(ERROR) << "Input length is zero"; 90 | return false; 91 | } 92 | 93 | if (!IsEapKbl(p_Input, p_InputLen)) { 94 | LOG(ERROR) << "File is not EAP KBL"; 95 | return false; 96 | } 97 | 98 | EapKblHeader *s_Header{reinterpret_cast(const_cast(p_Input))}; 99 | 100 | VLOG(1) << "s_Header->version:\n" << HexDump(&s_Header->version, sizeof(s_Header->version)); 101 | VLOG(1) << "s_Header->type:\n" << HexDump(&s_Header->type, sizeof(s_Header->type)); 102 | VLOG(1) << "s_Header->header_size:\n" << HexDump(&s_Header->header_size, sizeof(s_Header->header_size)); 103 | VLOG(1) << "s_Header->body_size:\n" << HexDump(&s_Header->body_size, sizeof(s_Header->body_size)); 104 | VLOG(1) << "s_Header->entry_point:\n" << HexDump(&s_Header->entry_point, sizeof(s_Header->entry_point)); 105 | VLOG(1) << "s_Header->base_address:\n" << HexDump(&s_Header->base_address, sizeof(s_Header->base_address)); 106 | VLOG(1) << "s_Header->fill_pattern:\n" << HexDump(&s_Header->fill_pattern, sizeof(s_Header->fill_pattern)); 107 | VLOG(1) << "s_Header->key_seed:\n" << HexDump(&s_Header->key_seed, sizeof(s_Header->key_seed)); 108 | 109 | // TODO: Determine if the correct keyset can be deduced without brute forcing 110 | std::vector s_SouthbridgeRevisions{"AEOLIA", "BELIZE", "BELIZE 2", "BAIKAL"}; 111 | for (const std::string &l_Revision : s_SouthbridgeRevisions) { 112 | // Create copy of s_Header to use in loop 113 | EapKblHeader l_Header = *s_Header; 114 | 115 | VLOG(1) << "Testing keys for " << l_Revision; 116 | 117 | GetKeyToVariable("EAP", "KBL_AES_KEY", l_Revision, s_AesKey); 118 | GetKeyToVariable("EAP", "KBL_IV", l_Revision, s_Iv); 119 | GetKeyToVariable("EAP", "KBL_MAC_KEY", l_Revision, s_MacKey); 120 | 121 | VLOG(3) << "EAP_KBL_AES_KEY:\n" << HexDump(s_AesKey, sizeof(s_AesKey)); 122 | VLOG(3) << "EAP_KBL_IV:\n" << HexDump(s_Iv, sizeof(s_Iv)); 123 | VLOG(3) << "EAP_KBL_MAC_KEY:\n" << HexDump(s_MacKey, sizeof(s_MacKey)); 124 | 125 | std::vector s_EncryptionHeader; 126 | if (AesDecryptCbc(s_AesKey, sizeof(s_AesKey), s_Iv, reinterpret_cast(s_Header) + sizeof(EapKblHeader) - sizeof(EapKblEncryptionHeader), sizeof(EapKblEncryptionHeader), s_EncryptionHeader) != sizeof(EapKblEncryptionHeader)) { 127 | LOG(ERROR) << "Error decrypting data with " << l_Revision << " keyset"; 128 | continue; 129 | } 130 | std::copy(s_EncryptionHeader.begin(), s_EncryptionHeader.end(), reinterpret_cast(&l_Header) + sizeof(EapKblHeader) - s_EncryptionHeader.size()); 131 | 132 | VLOG(1) << "s_Header->body_aes_key:\n" << HexDump(&l_Header.body_aes_key, sizeof(l_Header.body_aes_key)); 133 | VLOG(1) << "s_Header->body_hmac_key:\n" << HexDump(&l_Header.body_hmac_key, sizeof(l_Header.body_hmac_key)); 134 | VLOG(1) << "s_Header->body_hmac:\n" << HexDump(&l_Header.body_hmac, sizeof(l_Header.body_hmac)); 135 | VLOG(1) << "s_Header->header_hmac:\n" << HexDump(&l_Header.header_hmac, sizeof(l_Header.header_hmac)); 136 | 137 | std::vector s_HeaderHmacReal; 138 | if (!HmacSha1(s_MacKey, sizeof(s_MacKey), reinterpret_cast(&l_Header), sizeof(EapKblHeader) - sizeof(l_Header.header_hmac), s_HeaderHmacReal)) { 139 | LOG(WARNING) << "Error calculating header HMAC digest with " << l_Revision << " keyset"; 140 | continue; 141 | } 142 | VLOG(2) << "s_HeaderHmacReal:\n" << HexDump(&s_HeaderHmacReal[0], s_HeaderHmacReal.size()); 143 | 144 | if (std::memcmp(l_Header.header_hmac, &s_HeaderHmacReal[0], sizeof(l_Header.header_hmac)) != 0) { 145 | LOG(WARNING) << "Header HMAC does not match with " << l_Revision << " keyset"; 146 | continue; 147 | } 148 | 149 | if (AesDecryptCbc(l_Header.body_aes_key, sizeof(l_Header.body_aes_key), s_Iv, p_Input + sizeof(EapKblHeader), l_Header.body_size, p_Output) != l_Header.body_size) { 150 | LOG(ERROR) << "Error decrypting data with " << l_Revision << " keyset"; 151 | p_Output.clear(); 152 | p_Output.shrink_to_fit(); 153 | continue; 154 | } 155 | 156 | std::vector s_BodyHmacReal; 157 | if (!HmacSha1(l_Header.body_hmac_key, sizeof(l_Header.body_hmac_key), p_Input + sizeof(EapKblHeader), l_Header.body_size, s_BodyHmacReal)) { 158 | LOG(WARNING) << "Error calculating body HMAC digest with " << l_Revision << " keyset"; 159 | continue; 160 | } 161 | VLOG(2) << "s_BodyHmacReal:\n" << HexDump(&s_BodyHmacReal[0], s_BodyHmacReal.size()); 162 | 163 | if (std::memcmp(l_Header.body_hmac, &s_BodyHmacReal[0], sizeof(l_Header.body_hmac)) != 0) { 164 | LOG(WARNING) << "Body HMAC does not match with " << l_Revision << " keyset"; 165 | continue; 166 | } 167 | 168 | LOG(INFO) << "Southbridge keyset is " << l_Revision; 169 | return true; 170 | } 171 | 172 | LOG(ERROR) << "Could not find correct Southbridge keyset"; 173 | return false; 174 | } 175 | 176 | bool DecryptKernel(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 177 | if (p_Input == &p_Output[0]) { 178 | LOG(ERROR) << "Input is at the same location as output"; 179 | return false; 180 | } 181 | 182 | p_Output.clear(); 183 | p_Output.shrink_to_fit(); 184 | 185 | if (p_Input == nullptr) { 186 | LOG(ERROR) << "Input data is NULL"; 187 | return false; 188 | } 189 | if (p_InputLen == 0) { 190 | LOG(ERROR) << "Input length is zero"; 191 | return false; 192 | } 193 | 194 | if (!IsEapKernel(p_Input, p_InputLen)) { 195 | LOG(ERROR) << "File is not EAP kernel"; 196 | return false; 197 | } 198 | 199 | EapKernelHeader *s_StorageHeader{reinterpret_cast(const_cast(p_Input))}; 200 | 201 | VLOG(1) << "s_StorageHeader->storage_version:\n" << HexDump(&s_StorageHeader->version, sizeof(s_StorageHeader->version)); 202 | VLOG(1) << "s_StorageHeader->storage_iv:\n" << HexDump(&s_StorageHeader->iv, sizeof(s_StorageHeader->iv)); 203 | VLOG(1) << "s_StorageHeader->storage_digest:\n" << HexDump(&s_StorageHeader->digest, sizeof(s_StorageHeader->digest)); 204 | 205 | // TODO: Determine if the correct keyset can be deduced without brute forcing 206 | // 0 = Proto, 1 = 1.00, 2 = Old, 3 = New 207 | for (uint8_t i{0}; i <= 3; i++) { 208 | std::vector s_Storage; 209 | 210 | GetKeyToVariable("EAP", "KERNEL_ENC_KEY", i, s_EncKey); 211 | GetKeyToVariable("EAP", "KERNEL_MAC_KEY", i, s_MacKey); 212 | 213 | VLOG(3) << "EAP_KERNEL_ENC_KEY:\n" << HexDump(s_EncKey, sizeof(s_EncKey)); 214 | VLOG(3) << "EAP_KERNEL_MAC_KEY:\n" << HexDump(s_MacKey, sizeof(s_MacKey)); 215 | 216 | constexpr size_t s_EncStorageSize{c_SectorSize_ - sizeof(EapKernelHeader)}; 217 | 218 | if (AesDecryptCbcCts(s_EncKey, sizeof(s_EncKey), s_StorageHeader->iv, p_Input + sizeof(EapKernelHeader), s_EncStorageSize, s_Storage) != s_EncStorageSize) { 219 | LOG(WARNING) << "Error decrypting data with keyset #" << static_cast(i) << ", attempting next keyset"; 220 | continue; 221 | } 222 | 223 | std::vector s_StorageDigestReal; 224 | if (!HmacSha1(s_MacKey, sizeof(s_MacKey), &s_Storage[0], s_Storage.size(), s_StorageDigestReal)) { 225 | LOG(WARNING) << "Error calculating storage digest with keyset #" << static_cast(i) << ", attempting next keyset"; 226 | continue; 227 | } 228 | VLOG(2) << "s_StorageDigestReal:\n" << HexDump(&s_StorageDigestReal[0], s_StorageDigestReal.size()); 229 | 230 | if (std::memcmp(s_StorageHeader->digest, &s_StorageDigestReal[0], sizeof(s_StorageHeader->digest)) != 0) { 231 | LOG(WARNING) << "Invalid storage header digest with keyset #" << static_cast(i) << ", attempting next keyset"; 232 | continue; 233 | } 234 | 235 | EapKernelBodyInfo *s_BodyInfoHeader{reinterpret_cast(const_cast(&s_Storage[0]))}; 236 | if (!CheckMagic(s_BodyInfoHeader->magic, c_EapBodyInfoHeaderMagic_, sizeof(c_EapBodyInfoHeaderMagic_))) { 237 | LOG(ERROR) << "Invalid magic"; 238 | continue; 239 | } 240 | VLOG(1) << "s_BodyInfoHeader->size: " << static_cast(s_BodyInfoHeader->size); 241 | VLOG(1) << "s_BodyInfoHeader->offset: " << hex(s_BodyInfoHeader->offset); 242 | 243 | EapKernelHeader *s_BodyHeader{reinterpret_cast(const_cast(p_Input + s_BodyInfoHeader->offset))}; 244 | 245 | if (!CheckMagic(s_BodyHeader->magic, c_EapBodyHeaderMagic_, sizeof(c_EapBodyHeaderMagic_))) { 246 | LOG(ERROR) << "Invalid magic"; 247 | continue; 248 | } 249 | 250 | if (s_BodyHeader->version != c_ExpectedVersion_) { 251 | LOG(ERROR) << "Invalid version"; 252 | continue; 253 | } 254 | 255 | VLOG(1) << "s_BodyHeader->version:\n" << HexDump(&s_BodyHeader->version, sizeof(s_BodyHeader->version)); 256 | VLOG(1) << "s_BodyHeader->iv:\n" << HexDump(&s_BodyHeader->iv, sizeof(s_BodyHeader->iv)); 257 | VLOG(1) << "s_BodyHeader->digest:\n" << HexDump(&s_BodyHeader->digest, sizeof(s_BodyHeader->digest)); 258 | 259 | if (AesDecryptCbcCts(s_EncKey, sizeof(s_EncKey), s_BodyHeader->iv, p_Input + s_BodyInfoHeader->offset + sizeof(EapKernelHeader), s_BodyInfoHeader->size - sizeof(EapKernelHeader), p_Output) != s_BodyInfoHeader->size - sizeof(EapKernelHeader)) { 260 | LOG(ERROR) << "Error decrypting data"; 261 | p_Output.clear(); 262 | p_Output.shrink_to_fit(); 263 | continue; 264 | } 265 | 266 | std::vector s_BodyDigestReal; 267 | if (!HmacSha1(s_MacKey, sizeof(s_MacKey), &p_Output[0], p_Output.size(), s_BodyDigestReal)) { 268 | LOG(WARNING) << "Error calculating body digest"; 269 | continue; 270 | } 271 | VLOG(2) << "s_BodyDigestReal:\n" << HexDump(&s_BodyDigestReal[0], s_BodyDigestReal.size()); 272 | 273 | if (std::memcmp(s_BodyHeader->digest, &s_BodyDigestReal[0], sizeof(s_BodyHeader->digest)) != 0) { 274 | LOG(WARNING) << "Invalid body header digest"; 275 | continue; 276 | } 277 | 278 | LOG(INFO) << "EAP kernel uses keyset #" << static_cast(i); 279 | return true; 280 | } 281 | 282 | LOG(ERROR) << "Could not find correct EAP kernel keyset"; 283 | return false; 284 | } 285 | 286 | bool EncryptKbl(const unsigned char *p_Input, size_t p_InputLen, std::string p_SouthbridgeRevision, std::vector &p_Output) { 287 | if (p_Input == &p_Output[0]) { 288 | LOG(ERROR) << "Input is at the same location as output"; 289 | return false; 290 | } 291 | 292 | p_Output.clear(); 293 | p_Output.shrink_to_fit(); 294 | 295 | if (p_Input == nullptr) { 296 | LOG(ERROR) << "Input data is NULL"; 297 | return false; 298 | } 299 | if (p_InputLen == 0 || p_InputLen > 0x7FF80) { // Max size is 0x80000, packing adds an additional 0x80 bytes 300 | LOG(ERROR) << "Input length is invalid"; 301 | return false; 302 | } 303 | if (p_SouthbridgeRevision != "AEOLIA" && p_SouthbridgeRevision != "BELIZE" && p_SouthbridgeRevision != "BELIZE 2" && p_SouthbridgeRevision != "BAIKAL") { 304 | LOG(ERROR) << "Invalid southbridge revision"; 305 | return false; 306 | } 307 | 308 | if (!CheckMagic(p_Input, c_EapElfMagic_, sizeof(c_EapElfMagic_))) { 309 | LOG(ERROR) << "Invalid magic"; 310 | return false; 311 | } 312 | 313 | unsigned char s_BodyKey[AES_BLOCK_SIZE]; // Flawfinder: ignore 314 | if (!RAND_bytes(s_BodyKey, AES_BLOCK_SIZE)) { 315 | LOG(ERROR) << "Unable to generate new body AES key"; 316 | return false; 317 | } 318 | 319 | unsigned char s_BodyIv[AES_BLOCK_SIZE]; // Flawfinder: ignore 320 | std::memset(s_BodyIv, '\0', AES_BLOCK_SIZE); 321 | 322 | unsigned char s_BodyHmacKey[AES_BLOCK_SIZE]; // Flawfinder: ignore 323 | if (!RAND_bytes(s_BodyHmacKey, AES_BLOCK_SIZE)) { 324 | LOG(ERROR) << "Unable to generate new body MAC key"; 325 | return false; 326 | } 327 | 328 | std::vector s_EncryptedBody; 329 | if (AesEncryptCbc(s_BodyKey, AES_BLOCK_SIZE, s_BodyIv, p_Input, p_InputLen, s_EncryptedBody) != p_InputLen) { 330 | LOG(ERROR) << "Error decrypting data"; 331 | return false; 332 | } 333 | 334 | std::vector s_BodyHmac; 335 | if (!HmacSha1(s_BodyHmacKey, AES_BLOCK_SIZE, &s_EncryptedBody[0], s_EncryptedBody.size(), s_BodyHmac)) { 336 | LOG(ERROR) << "Error calculating body HMAC digest"; 337 | return false; 338 | } 339 | 340 | EapKblHeader s_Header; 341 | std::copy(c_EapKblMagic_, c_EapKblMagic_ + sizeof(s_Header.magic), s_Header.magic); 342 | s_Header.version = 1; 343 | s_Header.type = 0x6801; // TODO: Is this calculated by version/item, or static 344 | s_Header.entry_point = 1677721600; // TODO: Is this calculated by version/item, or static 345 | s_Header.base_address = 1677721600; // TODO: Is this calculated by version/item, or static 346 | s_Header.body_size = static_cast(p_InputLen); // An oversized size_t is indirectly checked for earlier in this function 347 | s_Header.header_size = sizeof(EapKblHeader); 348 | std::copy(c_FillPattern_, c_FillPattern_ + sizeof(s_Header.fill_pattern), s_Header.fill_pattern); // TODO: Is this calculated by version/item, or static 349 | std::copy(c_KeySeed_, c_KeySeed_ + sizeof(s_Header.key_seed), s_Header.key_seed); // TODO: Is this calculated by version/item, or static 350 | 351 | std::copy(s_BodyKey, s_BodyKey + sizeof(s_Header.body_aes_key), s_Header.body_aes_key); 352 | std::copy(s_BodyHmacKey, s_BodyHmacKey + sizeof(s_Header.body_hmac_key), s_Header.body_hmac_key); 353 | std::copy(s_BodyHmac.begin(), s_BodyHmac.begin() + sizeof(s_Header.body_hmac), s_Header.body_hmac); 354 | 355 | GetKeyToVariable("EAP", "KBL_AES_KEY", p_SouthbridgeRevision, s_AesKey); 356 | GetKeyToVariable("EAP", "KBL_IV", p_SouthbridgeRevision, s_Iv); 357 | GetKeyToVariable("EAP", "KBL_MAC_KEY", p_SouthbridgeRevision, s_MacKey); 358 | 359 | VLOG(3) << "EAP_IPL_AES_KEY:\n" << HexDump(s_AesKey, sizeof(s_AesKey)); 360 | VLOG(3) << "EAP_IPL_IV:\n" << HexDump(s_Iv, sizeof(s_Iv)); 361 | VLOG(3) << "EAP_KBL_MAC_KEY:\n" << HexDump(s_MacKey, sizeof(s_MacKey)); 362 | 363 | std::vector s_HeaderHmac; 364 | if (!HmacSha1(s_MacKey, sizeof(s_MacKey), reinterpret_cast(&s_Header), sizeof(EapKblHeader) - sizeof(s_Header.header_hmac), s_HeaderHmac)) { 365 | LOG(ERROR) << "Error calculating header HMAC digest with " << p_SouthbridgeRevision << " keyset"; 366 | return false; 367 | } 368 | 369 | std::copy(s_HeaderHmac.begin(), s_HeaderHmac.begin() + sizeof(s_Header.header_hmac), s_Header.header_hmac); 370 | 371 | VLOG(1) << "s_Header->version:\n" << HexDump(&s_Header.version, sizeof(s_Header.version)); 372 | VLOG(1) << "s_Header->type:\n" << HexDump(&s_Header.type, sizeof(s_Header.type)); 373 | VLOG(1) << "s_Header->header_size:\n" << HexDump(&s_Header.header_size, sizeof(s_Header.header_size)); 374 | VLOG(1) << "s_Header->body_size:\n" << HexDump(&s_Header.body_size, sizeof(s_Header.body_size)); 375 | VLOG(1) << "s_Header->entry_point:\n" << HexDump(&s_Header.entry_point, sizeof(s_Header.entry_point)); 376 | VLOG(1) << "s_Header->base_address:\n" << HexDump(&s_Header.base_address, sizeof(s_Header.base_address)); 377 | VLOG(1) << "s_Header->fill_pattern:\n" << HexDump(&s_Header.fill_pattern, sizeof(s_Header.fill_pattern)); 378 | VLOG(1) << "s_Header->key_seed:\n" << HexDump(&s_Header.key_seed, sizeof(s_Header.key_seed)); 379 | VLOG(1) << "s_Header->body_aes_key:\n" << HexDump(&s_Header.body_aes_key, sizeof(s_Header.body_aes_key)); 380 | VLOG(1) << "s_Header->body_hmac_key:\n" << HexDump(&s_Header.body_hmac_key, sizeof(s_Header.body_hmac_key)); 381 | VLOG(1) << "s_Header->body_hmac:\n" << HexDump(&s_Header.body_hmac, sizeof(s_Header.body_hmac)); 382 | VLOG(1) << "s_Header->header_hmac:\n" << HexDump(&s_Header.header_hmac, sizeof(s_Header.header_hmac)); 383 | 384 | std::vector s_EncryptedHeader; 385 | AesEncryptCbc(s_AesKey, sizeof(s_AesKey), s_Iv, reinterpret_cast(&s_Header) + sizeof(EapKblHeader) - sizeof(EapKblEncryptionHeader), sizeof(EapKblEncryptionHeader), s_EncryptedHeader); 386 | std::copy(s_EncryptedHeader.begin(), s_EncryptedHeader.end(), reinterpret_cast(&s_Header) + sizeof(EapKblHeader) - s_EncryptedHeader.size()); 387 | 388 | p_Output.insert(p_Output.end(), reinterpret_cast(&s_Header), reinterpret_cast(&s_Header) + sizeof(s_Header)); 389 | p_Output.insert(p_Output.end(), s_EncryptedBody.begin(), s_EncryptedBody.end()); 390 | 391 | return true; 392 | } 393 | 394 | bool EncryptKernel(const unsigned char *p_Input, size_t p_InputLen, uint32_t p_KeysetNumber, std::vector &p_Output) { 395 | if (p_Input == &p_Output[0]) { 396 | LOG(ERROR) << "Input is at the same location as output"; 397 | return false; 398 | } 399 | 400 | p_Output.clear(); 401 | p_Output.shrink_to_fit(); 402 | 403 | if (p_Input == nullptr) { 404 | LOG(ERROR) << "Input data is NULL"; 405 | return false; 406 | } 407 | if (p_InputLen == 0 || p_InputLen > UINT32_MAX) { // TODO: Check for ACTUAL valid max length 408 | LOG(ERROR) << "Input length is invalid"; 409 | return false; 410 | } 411 | if (/*p_KeysetNumber < 0 ||*/ p_KeysetNumber > 3) { 412 | LOG(ERROR) << "Invalid keyset selection"; 413 | return false; 414 | } 415 | 416 | if (!CheckMagic(p_Input, c_EapElfMagic_, sizeof(c_EapElfMagic_))) { 417 | LOG(ERROR) << "Invalid magic"; 418 | return false; 419 | } 420 | 421 | EapKernelHeader s_BodyHeader; 422 | std::copy(c_EapBodyHeaderMagic_, c_EapBodyHeaderMagic_ + sizeof(s_BodyHeader.magic), s_BodyHeader.magic); 423 | s_BodyHeader.version = c_ExpectedVersion_; 424 | 425 | if (!RAND_bytes(s_BodyHeader.iv, AES_BLOCK_SIZE)) { 426 | LOG(ERROR) << "Unable to generate new body AES key"; 427 | return false; 428 | } 429 | 430 | GetKeyToVariable("EAP", "KERNEL_ENC_KEY", p_KeysetNumber, s_EncKey); 431 | GetKeyToVariable("EAP", "KERNEL_MAC_KEY", p_KeysetNumber, s_MacKey); 432 | 433 | VLOG(3) << "EAP_KERNEL_MAC_KEY:\n" << HexDump(s_MacKey, sizeof(s_MacKey)); 434 | VLOG(3) << "EAP_KERNEL_ENC_KEY:\n" << HexDump(s_EncKey, sizeof(s_EncKey)); 435 | 436 | std::vector s_BodyHmac; 437 | if (!HmacSha1(s_MacKey, sizeof(s_MacKey), p_Input, p_InputLen, s_BodyHmac)) { 438 | LOG(WARNING) << "Error calculating body digest"; 439 | return false; 440 | } 441 | std::copy(s_BodyHmac.begin(), s_BodyHmac.begin() + sizeof(s_BodyHeader.digest), s_BodyHeader.digest); 442 | VLOG(1) << "s_BodyHeader.digest:\n" << HexDump(s_BodyHeader.digest, sizeof(s_BodyHeader.digest)); 443 | 444 | std::vector s_Body; 445 | if (AesEncryptCbcCts(s_EncKey, sizeof(s_EncKey), s_BodyHeader.iv, p_Input, p_InputLen, s_Body) != p_InputLen) { 446 | LOG(ERROR) << "Error Encrypting data"; 447 | return false; 448 | } 449 | 450 | EapKernelBodyInfo s_BodyInfoHeader; 451 | std::copy(c_EapBodyInfoHeaderMagic_, c_EapBodyInfoHeaderMagic_ + sizeof(s_BodyInfoHeader.magic), s_BodyInfoHeader.magic); 452 | s_BodyInfoHeader.size = s_Body.size() + sizeof(EapKernelHeader); 453 | s_BodyInfoHeader.offset = c_SectorSize_; 454 | 455 | VLOG(1) << "s_BodyInfoHeader.size: " << static_cast(s_BodyInfoHeader.size); 456 | VLOG(1) << "s_BodyInfoHeader.offset: " << hex(s_BodyInfoHeader.offset); 457 | 458 | constexpr size_t s_StorageSize{c_SectorSize_ - sizeof(EapKernelHeader)}; 459 | std::vector s_Storage(s_StorageSize, '\0'); 460 | std::copy(s_Storage.begin(), s_Storage.begin() + sizeof(s_BodyInfoHeader), reinterpret_cast(&s_BodyInfoHeader)); 461 | 462 | std::vector s_EncStorage; 463 | if (AesEncryptCbcCts(s_EncKey, sizeof(s_EncKey), s_BodyHeader.iv, &s_Storage[0], s_StorageSize, s_EncStorage) != s_StorageSize) { 464 | LOG(ERROR) << "Error Encrypting data"; 465 | return false; 466 | } 467 | 468 | EapKernelHeader s_StorageHeader; 469 | std::copy(c_EapKernelMagic_, c_EapKernelMagic_ + sizeof(s_StorageHeader.magic), s_StorageHeader.magic); 470 | s_StorageHeader.version = c_ExpectedVersion_; 471 | std::copy(s_BodyHeader.iv, s_BodyHeader.iv + sizeof(s_StorageHeader.iv), s_StorageHeader.iv); 472 | std::vector s_StorageHmac; 473 | if (!HmacSha1(s_MacKey, sizeof(s_MacKey), &s_Storage[0], s_Storage.size(), s_StorageHmac)) { 474 | LOG(WARNING) << "Error calculating storage digest"; 475 | return false; 476 | } 477 | std::copy(s_StorageHmac.begin(), s_StorageHmac.begin() + sizeof(s_StorageHeader.digest), s_StorageHeader.digest); 478 | VLOG(1) << "s_StorageHeader.digest:\n" << HexDump(s_StorageHeader.digest, sizeof(s_StorageHeader.digest)); 479 | 480 | p_Output.insert(p_Output.begin(), reinterpret_cast(&s_StorageHeader), reinterpret_cast(&s_StorageHeader) + sizeof(s_StorageHeader)); 481 | p_Output.insert(p_Output.end(), s_EncStorage.begin(), s_EncStorage.end()); 482 | p_Output.insert(p_Output.end(), reinterpret_cast(&s_BodyHeader), reinterpret_cast(&s_BodyHeader) + sizeof(s_BodyHeader)); 483 | p_Output.insert(p_Output.end(), s_Body.begin(), s_Body.end()); 484 | 485 | // TODO: Zero pad? Or is the padding I see an unrelated issue in the test file (BLS extraction) 486 | 487 | return true; 488 | } 489 | } // namespace eap 490 | -------------------------------------------------------------------------------- /src/crypto.cc: -------------------------------------------------------------------------------- 1 | #include "crypto.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "banned.h" 19 | 20 | template 21 | inline I align_up(I x, I align) { 22 | auto y{std::max(x, align)}; 23 | return (y + (align - 1)) & ~(align - 1); 24 | } 25 | 26 | template 27 | inline I align_down(I x, I align) { 28 | auto y{std::max(x, align)}; 29 | return y & ~(align - 1); 30 | } 31 | 32 | bool RsaPkcs1V15Verify(const unsigned char *p_Modulus, size_t p_ModulusLen, const unsigned char *p_PublicExponent, size_t p_PublicExponentLen, const unsigned char *p_Input, size_t p_InputLen, const unsigned char *p_Signature, size_t p_SignatureLen) { 33 | if (p_Modulus == nullptr) { 34 | LOG(ERROR) << "Modulus data is NULL"; 35 | return false; 36 | } 37 | if (p_ModulusLen == 0) { 38 | LOG(ERROR) << "Modulus length is zero"; 39 | return false; 40 | } 41 | if (p_PublicExponent == nullptr) { 42 | LOG(ERROR) << "Public Exponent data is NULL"; 43 | return false; 44 | } 45 | if (p_PublicExponentLen == 0) { 46 | LOG(ERROR) << "Public Exponent length is zero"; 47 | return false; 48 | } 49 | if (p_Input == nullptr) { 50 | LOG(ERROR) << "Input data is NULL"; 51 | return false; 52 | } 53 | if (p_InputLen == 0) { 54 | LOG(ERROR) << "Input length is zero"; 55 | return false; 56 | } 57 | if (p_Signature == nullptr) { 58 | LOG(ERROR) << "Signature data is NULL"; 59 | return false; 60 | } 61 | if (p_SignatureLen == 0) { 62 | LOG(ERROR) << "Signature length is zero"; 63 | return false; 64 | } 65 | 66 | EVP_MD_CTX *s_Ctx{EVP_MD_CTX_new()}; 67 | EVP_PKEY *s_Pkey{EVP_PKEY_new()}; 68 | RSA *s_Rsa{RSA_new()}; 69 | BIGNUM *s_BnModulus{BN_new()}; 70 | BIGNUM *s_BnPublicExponent{BN_new()}; 71 | 72 | if (s_Ctx == nullptr) { 73 | LOG(ERROR) << "EVP_MD_CTX_new"; 74 | goto fail; 75 | } 76 | if (s_Pkey == nullptr) { 77 | LOG(ERROR) << "EVP_PKEY_new"; 78 | goto fail; 79 | } 80 | if (s_Rsa == nullptr) { 81 | LOG(ERROR) << "RSA_new"; 82 | goto fail; 83 | } 84 | if (s_BnModulus == nullptr) { 85 | LOG(ERROR) << "BN_new"; 86 | goto fail; 87 | } 88 | if (s_BnPublicExponent == nullptr) { 89 | LOG(ERROR) << "BN_new"; 90 | goto fail; 91 | } 92 | 93 | BN_bin2bn(p_Modulus, p_ModulusLen, s_BnModulus); 94 | BN_bin2bn(p_PublicExponent, p_PublicExponentLen, s_BnPublicExponent); 95 | 96 | RSA_set0_key(s_Rsa, BN_dup(s_BnModulus), BN_dup(s_BnPublicExponent), NULL); 97 | 98 | BN_free(s_BnModulus); // Does not need `if (s_BnModulus != nullptr)` as it's checked against nullptr above 99 | s_BnModulus = nullptr; 100 | BN_free(s_BnPublicExponent); // Does not need `if (s_BnPublicExponent != nullptr)` as it's checked against nullptr above 101 | s_BnPublicExponent = nullptr; 102 | 103 | if (EVP_PKEY_assign_RSA(s_Pkey, RSAPublicKey_dup(s_Rsa)) != 1) { 104 | LOG(ERROR) << "EVP_PKEY_assign_RSA"; 105 | goto fail; 106 | } 107 | 108 | RSA_free(s_Rsa); // Does not need `if (s_Rsa != nullptr)` as it's checked against NULL above 109 | s_Rsa = nullptr; 110 | 111 | if (EVP_DigestVerifyInit(s_Ctx, NULL, NULL, NULL, s_Pkey) != 1) { 112 | LOG(ERROR) << "EVP_DigestVerifyInit"; 113 | goto fail; 114 | } 115 | 116 | if (EVP_DigestVerifyUpdate(s_Ctx, p_Input, p_InputLen) != 1) { 117 | LOG(ERROR) << "EVP_DigestVerifyUpdate"; 118 | goto fail; 119 | } 120 | 121 | if (EVP_DigestVerifyFinal(s_Ctx, p_Signature, p_SignatureLen) != 1) { 122 | LOG(ERROR) << "EVP_DigestVerifyFinal"; 123 | goto fail; 124 | } 125 | 126 | EVP_PKEY_free(s_Pkey); // Does not need `if (s_Pkey != nullptr)` as it's checked against nullptr above 127 | s_Pkey = nullptr; 128 | EVP_MD_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 129 | s_Ctx = nullptr; 130 | 131 | return true; 132 | 133 | fail: 134 | if (s_BnModulus != nullptr) { 135 | BN_free(s_BnModulus); 136 | s_BnModulus = nullptr; 137 | } 138 | if (s_BnPublicExponent != nullptr) { 139 | BN_free(s_BnPublicExponent); 140 | s_BnPublicExponent = nullptr; 141 | } 142 | if (s_Rsa != nullptr) { 143 | RSA_free(s_Rsa); 144 | s_Rsa = nullptr; 145 | } 146 | if (s_Pkey != nullptr) { 147 | EVP_PKEY_free(s_Pkey); 148 | s_Pkey = nullptr; 149 | } 150 | if (s_Ctx != nullptr) { 151 | EVP_MD_CTX_free(s_Ctx); 152 | s_Ctx = nullptr; 153 | } 154 | 155 | return false; 156 | } 157 | 158 | bool RsaPublicEncrypt(const unsigned char *p_Modulus, size_t p_ModulusLen, const unsigned char *p_PublicExponent, size_t p_PublicExponentLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, size_t p_OutputLen, int32_t p_Padding) { 159 | if (p_Input == &p_Output[0]) { 160 | LOG(ERROR) << "Input is at the same location as output"; 161 | return false; 162 | } 163 | 164 | p_Output.clear(); 165 | p_Output.shrink_to_fit(); 166 | 167 | if (p_Modulus == nullptr) { 168 | LOG(ERROR) << "Modulus data is NULL"; 169 | return false; 170 | } 171 | if (p_ModulusLen == 0) { 172 | LOG(ERROR) << "Modulus length is zero"; 173 | return false; 174 | } 175 | if (p_PublicExponent == nullptr) { 176 | LOG(ERROR) << "Public Exponent data is NULL"; 177 | return false; 178 | } 179 | if (p_PublicExponentLen == 0) { 180 | LOG(ERROR) << "Public Exponent length is zero"; 181 | return false; 182 | } 183 | if (p_Input == nullptr) { 184 | LOG(ERROR) << "Input data is NULL"; 185 | return false; 186 | } 187 | if (p_InputLen == 0) { 188 | LOG(ERROR) << "Input length is zero"; 189 | return false; 190 | } 191 | if (p_OutputLen == 0) { 192 | LOG(ERROR) << "Output length is zero"; 193 | return false; 194 | } 195 | // The pad parameter can take the following values: 196 | // - RSA_PKCS1_PADDING for PKCS#1 padding 197 | // - RSA_NO_PADDING for no padding 198 | // - RSA_PKCS1_OAEP_PADDING for OAEP padding (encrypt and decrypt only) 199 | // - RSA_X931_PADDING for X9.31 padding (signature operations only) 200 | // - RSA_PKCS1_PSS_PADDING (sign and verify only) 201 | // - RSA_PKCS1_WITH_TLS_PADDING for TLS RSA ClientKeyExchange message padding (decryption only) 202 | if (p_Padding != RSA_PKCS1_PADDING && p_Padding != RSA_NO_PADDING && p_Padding != RSA_PKCS1_OAEP_PADDING) { 203 | LOG(ERROR) << "Invalid padding option"; 204 | return false; 205 | } 206 | 207 | EVP_PKEY_CTX *s_Ctx{nullptr}; 208 | EVP_PKEY *s_Pkey{EVP_PKEY_new()}; 209 | RSA *s_Rsa{RSA_new()}; 210 | BIGNUM *s_BnModulus{BN_new()}; 211 | BIGNUM *s_BnPublicExponent{BN_new()}; 212 | p_Output.resize(p_OutputLen, '\0'); 213 | 214 | if (s_Pkey == nullptr) { 215 | LOG(ERROR) << "EVP_PKEY_new"; 216 | goto fail; 217 | } 218 | if (s_Rsa == nullptr) { 219 | LOG(ERROR) << "RSA_new"; 220 | goto fail; 221 | } 222 | if (s_BnModulus == nullptr) { 223 | LOG(ERROR) << "BN_new"; 224 | goto fail; 225 | } 226 | if (s_BnPublicExponent == nullptr) { 227 | LOG(ERROR) << "BN_new"; 228 | goto fail; 229 | } 230 | 231 | BN_bin2bn(p_Modulus, p_ModulusLen, s_BnModulus); 232 | BN_bin2bn(p_PublicExponent, p_PublicExponentLen, s_BnPublicExponent); 233 | 234 | RSA_set0_key(s_Rsa, BN_dup(s_BnModulus), BN_dup(s_BnPublicExponent), NULL); 235 | 236 | BN_free(s_BnModulus); // Does not need `if (s_BnModulus != nullptr)` as it's checked against nullptr above 237 | s_BnModulus = nullptr; 238 | BN_free(s_BnPublicExponent); // Does not need `if (s_BnPublicExponent != nullptr)` as it's checked against nullptr above 239 | s_BnPublicExponent = nullptr; 240 | 241 | if (EVP_PKEY_assign_RSA(s_Pkey, RSAPublicKey_dup(s_Rsa)) != 1) { 242 | LOG(ERROR) << "EVP_PKEY_assign_RSA"; 243 | goto fail; 244 | } 245 | 246 | RSA_free(s_Rsa); // Does not need `if (s_Rsa != nullptr)` as it's checked against nullptr above 247 | s_Rsa = nullptr; 248 | 249 | s_Ctx = EVP_PKEY_CTX_new(s_Pkey, NULL); 250 | if (s_Ctx == NULL) { 251 | LOG(ERROR) << "EVP_PKEY_CTX_new"; 252 | goto fail; 253 | } 254 | 255 | if (EVP_PKEY_encrypt_init(s_Ctx) != 1) { 256 | LOG(ERROR) << "EVP_PKEY_encrypt_init"; 257 | goto fail; 258 | } 259 | 260 | if (EVP_PKEY_CTX_set_rsa_padding(s_Ctx, p_Padding) != 1) { 261 | LOG(ERROR) << "EVP_PKEY_CTX_set_rsa_padding"; 262 | goto fail; 263 | } 264 | 265 | if (EVP_PKEY_encrypt(s_Ctx, &p_Output[0], &p_OutputLen, p_Input, p_InputLen) != 1) { 266 | LOG(ERROR) << "EVP_PKEY_encrypt"; 267 | goto fail; 268 | } 269 | 270 | EVP_PKEY_free(s_Pkey); // Does not need `if (s_Pkey != nullptr)` as it's checked against nullptr above 271 | s_Pkey = nullptr; 272 | EVP_PKEY_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 273 | s_Ctx = nullptr; 274 | 275 | // Already unsigned 276 | // if (p_OutputLen < 0) { 277 | // return 0; 278 | // } 279 | 280 | return true; 281 | 282 | fail: 283 | p_Output.clear(); 284 | p_Output.shrink_to_fit(); 285 | 286 | if (s_BnModulus != nullptr) { 287 | BN_free(s_BnModulus); 288 | s_BnModulus = nullptr; 289 | } 290 | if (s_BnPublicExponent != nullptr) { 291 | BN_free(s_BnPublicExponent); 292 | s_BnPublicExponent = nullptr; 293 | } 294 | if (s_Rsa != nullptr) { 295 | RSA_free(s_Rsa); 296 | s_Rsa = nullptr; 297 | } 298 | if (s_Pkey != nullptr) { 299 | EVP_PKEY_free(s_Pkey); 300 | s_Pkey = nullptr; 301 | } 302 | if (s_Ctx != nullptr) { 303 | EVP_PKEY_CTX_free(s_Ctx); 304 | s_Ctx = nullptr; 305 | } 306 | 307 | return false; 308 | } 309 | 310 | size_t AesEncryptEcb(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, int32_t p_Padding) { 311 | if (p_Input == &p_Output[0]) { 312 | LOG(ERROR) << "Input is at the same location as output"; 313 | return 0; 314 | } 315 | 316 | p_Output.clear(); 317 | p_Output.shrink_to_fit(); 318 | 319 | if (p_Key == nullptr) { 320 | LOG(ERROR) << "Key data is NULL"; 321 | return 0; 322 | } 323 | if (p_KeyLen == 0) { 324 | LOG(ERROR) << "Key length is zero"; 325 | return 0; 326 | } 327 | if (p_Input == nullptr) { 328 | LOG(ERROR) << "Input data is NULL"; 329 | return 0; 330 | } 331 | if (p_InputLen == 0) { 332 | LOG(ERROR) << "Input length is zero"; 333 | return 0; 334 | } 335 | // TODO: Check padding values 336 | // By default encryption operations are padded using standard block padding 337 | // and the padding is checked and removed when decrypting. If the pad 338 | // parameter is zero then no padding is performed, the total amount of data 339 | // encrypted or decrypted must then be a multiple of the block size or an 340 | // error will occur. 341 | 342 | EVP_CIPHER_CTX *s_Ctx{EVP_CIPHER_CTX_new()}; 343 | int32_t s_Len{0}; 344 | int32_t s_CiphertextLen{0}; 345 | p_Output.resize(align_up(p_InputLen, static_cast(AES_BLOCK_SIZE)), '\0'); 346 | 347 | if (s_Ctx == nullptr) { 348 | LOG(ERROR) << "EVP_CIPHER_CTX_new"; 349 | goto fail; 350 | } 351 | 352 | if (p_KeyLen == 0x10) { 353 | if (EVP_EncryptInit_ex(s_Ctx, EVP_aes_128_ecb(), NULL, p_Key, NULL) != 1) { 354 | LOG(ERROR) << "EVP_EncryptInit_ex"; 355 | goto fail; 356 | } 357 | } else if (p_KeyLen == 0x18) { 358 | if (EVP_EncryptInit_ex(s_Ctx, EVP_aes_192_ecb(), NULL, p_Key, NULL) != 1) { 359 | LOG(ERROR) << "EVP_EncryptInit_ex"; 360 | goto fail; 361 | } 362 | } else if (p_KeyLen == 0x20) { 363 | if (EVP_EncryptInit_ex(s_Ctx, EVP_aes_256_ecb(), NULL, p_Key, NULL) != 1) { 364 | LOG(ERROR) << "EVP_EncryptInit_ex"; 365 | goto fail; 366 | } 367 | } else { 368 | LOG(ERROR) << "Unknown key size"; 369 | goto fail; 370 | } 371 | 372 | if (EVP_CIPHER_CTX_set_padding(s_Ctx, p_Padding) != 1) { 373 | LOG(ERROR) << "EVP_CIPHER_CTX_set_padding"; 374 | goto fail; 375 | } 376 | 377 | if (EVP_EncryptUpdate(s_Ctx, &p_Output[0], &s_Len, p_Input, p_InputLen) != 1) { 378 | LOG(ERROR) << "EVP_EncryptUpdate"; 379 | goto fail; 380 | } 381 | s_CiphertextLen = s_Len; 382 | 383 | if (EVP_EncryptFinal_ex(s_Ctx, &p_Output[s_Len], &s_Len) != 1) { 384 | LOG(ERROR) << "EVP_EncryptFinal_ex"; 385 | goto fail; 386 | } 387 | s_CiphertextLen += s_Len; 388 | 389 | EVP_CIPHER_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 390 | s_Ctx = nullptr; 391 | 392 | if (s_CiphertextLen < 0) { 393 | goto fail; 394 | } 395 | 396 | return s_CiphertextLen; 397 | 398 | fail: 399 | p_Output.clear(); 400 | p_Output.shrink_to_fit(); 401 | 402 | if (s_Ctx != nullptr) { 403 | EVP_CIPHER_CTX_free(s_Ctx); 404 | s_Ctx = nullptr; 405 | } 406 | 407 | return 0; 408 | } 409 | 410 | size_t AesDecryptEcb(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, int32_t p_Padding) { 411 | if (p_Input == &p_Output[0]) { 412 | LOG(ERROR) << "Input is at the same location as output"; 413 | return 0; 414 | } 415 | 416 | p_Output.clear(); 417 | p_Output.shrink_to_fit(); 418 | 419 | if (p_Key == nullptr) { 420 | LOG(ERROR) << "Key data is NULL"; 421 | return 0; 422 | } 423 | if (p_KeyLen == 0) { 424 | LOG(ERROR) << "Key length is zero"; 425 | return 0; 426 | } 427 | if (p_Input == nullptr) { 428 | LOG(ERROR) << "Input data is NULL"; 429 | return 0; 430 | } 431 | if (p_InputLen == 0) { 432 | LOG(ERROR) << "Input length is zero"; 433 | return 0; 434 | } 435 | // TODO: Check padding values 436 | // By default encryption operations are padded using standard block padding 437 | // and the padding is checked and removed when decrypting. If the pad 438 | // parameter is zero then no padding is performed, the total amount of data 439 | // encrypted or decrypted must then be a multiple of the block size or an 440 | // error will occur. 441 | 442 | EVP_CIPHER_CTX *s_Ctx{EVP_CIPHER_CTX_new()}; 443 | int32_t s_Len{0}; 444 | int32_t s_PlaintextLen{0}; 445 | p_Output.resize(align_up(p_InputLen, static_cast(AES_BLOCK_SIZE)), '\0'); 446 | 447 | if (s_Ctx == nullptr) { 448 | LOG(ERROR) << "EVP_CIPHER_CTX_new"; 449 | goto fail; 450 | } 451 | 452 | if (p_KeyLen == 0x10) { 453 | if (EVP_DecryptInit_ex(s_Ctx, EVP_aes_128_ecb(), NULL, p_Key, NULL) != 1) { 454 | LOG(ERROR) << "EVP_DecryptInit_ex"; 455 | goto fail; 456 | } 457 | } else if (p_KeyLen == 0x18) { 458 | if (EVP_DecryptInit_ex(s_Ctx, EVP_aes_192_ecb(), NULL, p_Key, NULL) != 1) { 459 | LOG(ERROR) << "EVP_DecryptInit_ex"; 460 | goto fail; 461 | } 462 | } else if (p_KeyLen == 0x20) { 463 | if (EVP_DecryptInit_ex(s_Ctx, EVP_aes_256_ecb(), NULL, p_Key, NULL) != 1) { 464 | LOG(ERROR) << "EVP_DecryptInit_ex"; 465 | goto fail; 466 | } 467 | } else { 468 | LOG(ERROR) << "Unknown key size"; 469 | goto fail; 470 | } 471 | 472 | if (EVP_CIPHER_CTX_set_padding(s_Ctx, p_Padding) != 1) { 473 | LOG(ERROR) << "EVP_CIPHER_CTX_set_padding"; 474 | goto fail; 475 | } 476 | 477 | if (EVP_DecryptUpdate(s_Ctx, &p_Output[0], &s_Len, p_Input, p_InputLen) != 1) { 478 | LOG(ERROR) << "EVP_DecryptUpdate"; 479 | goto fail; 480 | } 481 | s_PlaintextLen = s_Len; 482 | 483 | if (EVP_DecryptFinal_ex(s_Ctx, &p_Output[s_Len], &s_Len) != 1) { 484 | LOG(ERROR) << "EVP_DecryptFinal_ex"; 485 | goto fail; 486 | } 487 | s_PlaintextLen += s_Len; 488 | 489 | EVP_CIPHER_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 490 | s_Ctx = nullptr; 491 | 492 | if (s_PlaintextLen < 0) { 493 | goto fail; 494 | } 495 | 496 | return s_PlaintextLen; 497 | 498 | fail: 499 | p_Output.clear(); 500 | p_Output.shrink_to_fit(); 501 | 502 | if (s_Ctx != nullptr) { 503 | EVP_CIPHER_CTX_free(s_Ctx); 504 | s_Ctx = nullptr; 505 | } 506 | 507 | return 0; 508 | } 509 | 510 | size_t AesEncryptCbc(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Iv, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, int32_t p_Padding) { 511 | if (p_Input == &p_Output[0]) { 512 | LOG(ERROR) << "Input is at the same location as output"; 513 | return 0; 514 | } 515 | 516 | p_Output.clear(); 517 | p_Output.shrink_to_fit(); 518 | 519 | if (p_Key == nullptr) { 520 | LOG(ERROR) << "Key data is NULL"; 521 | return 0; 522 | } 523 | if (p_KeyLen == 0) { 524 | LOG(ERROR) << "Key length is zero"; 525 | return 0; 526 | } 527 | if (p_Iv == nullptr) { 528 | LOG(ERROR) << "IV is NULL"; 529 | return 0; 530 | } 531 | if (p_Input == nullptr) { 532 | LOG(ERROR) << "Input data is NULL"; 533 | return 0; 534 | } 535 | if (p_InputLen == 0) { 536 | LOG(ERROR) << "Input length is zero"; 537 | return 0; 538 | } 539 | // TODO: Check padding values 540 | // By default encryption operations are padded using standard block padding 541 | // and the padding is checked and removed when decrypting. If the pad 542 | // parameter is zero then no padding is performed, the total amount of data 543 | // encrypted or decrypted must then be a multiple of the block size or an 544 | // error will occur. 545 | 546 | EVP_CIPHER_CTX *s_Ctx{EVP_CIPHER_CTX_new()}; 547 | int32_t s_Len{0}; 548 | int32_t s_CiphertextLen{0}; 549 | p_Output.resize(align_up(p_InputLen, static_cast(AES_BLOCK_SIZE)), '\0'); 550 | 551 | if (s_Ctx == nullptr) { 552 | LOG(ERROR) << "EVP_CIPHER_CTX_new"; 553 | goto fail; 554 | } 555 | 556 | if (p_KeyLen == 0x10) { 557 | if (EVP_EncryptInit_ex(s_Ctx, EVP_aes_128_cbc(), NULL, p_Key, p_Iv) != 1) { 558 | LOG(ERROR) << "EVP_EncryptInit_ex"; 559 | goto fail; 560 | } 561 | } else if (p_KeyLen == 0x18) { 562 | if (EVP_EncryptInit_ex(s_Ctx, EVP_aes_192_cbc(), NULL, p_Key, p_Iv) != 1) { 563 | LOG(ERROR) << "EVP_EncryptInit_ex"; 564 | goto fail; 565 | } 566 | } else if (p_KeyLen == 0x20) { 567 | if (EVP_EncryptInit_ex(s_Ctx, EVP_aes_256_cbc(), NULL, p_Key, p_Iv) != 1) { 568 | LOG(ERROR) << "EVP_EncryptInit_ex"; 569 | goto fail; 570 | } 571 | } else { 572 | LOG(ERROR) << "Unknown key size"; 573 | goto fail; 574 | } 575 | 576 | if (EVP_CIPHER_CTX_set_padding(s_Ctx, p_Padding) != 1) { 577 | LOG(ERROR) << "EVP_CIPHER_CTX_set_padding"; 578 | goto fail; 579 | } 580 | 581 | if (EVP_EncryptUpdate(s_Ctx, &p_Output[0], &s_Len, p_Input, p_InputLen) != 1) { 582 | LOG(ERROR) << "EVP_EncryptUpdate"; 583 | goto fail; 584 | } 585 | s_CiphertextLen = s_Len; 586 | 587 | if (EVP_EncryptFinal_ex(s_Ctx, &p_Output[s_Len], &s_Len) != 1) { 588 | LOG(ERROR) << "EVP_EncryptFinal_ex"; 589 | goto fail; 590 | } 591 | s_CiphertextLen += s_Len; 592 | 593 | EVP_CIPHER_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 594 | s_Ctx = nullptr; 595 | 596 | if (s_CiphertextLen < 0) { 597 | goto fail; 598 | } 599 | 600 | return s_CiphertextLen; 601 | 602 | fail: 603 | p_Output.clear(); 604 | p_Output.shrink_to_fit(); 605 | 606 | if (s_Ctx != nullptr) { 607 | EVP_CIPHER_CTX_free(s_Ctx); 608 | s_Ctx = nullptr; 609 | } 610 | 611 | return 0; 612 | } 613 | 614 | size_t AesDecryptCbc(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Iv, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output, int32_t p_Padding) { 615 | if (p_Input == &p_Output[0]) { 616 | LOG(ERROR) << "Input is at the same location as output"; 617 | return 0; 618 | } 619 | 620 | p_Output.clear(); 621 | p_Output.shrink_to_fit(); 622 | 623 | if (p_Key == nullptr) { 624 | LOG(ERROR) << "Key data is NULL"; 625 | return 0; 626 | } 627 | if (p_KeyLen == 0) { 628 | LOG(ERROR) << "Key length is zero"; 629 | return 0; 630 | } 631 | if (p_Iv == nullptr) { 632 | LOG(ERROR) << "IV is NULL"; 633 | return 0; 634 | } 635 | if (p_Input == nullptr) { 636 | LOG(ERROR) << "Input data is NULL"; 637 | return 0; 638 | } 639 | if (p_InputLen == 0) { 640 | LOG(ERROR) << "Input length is zero"; 641 | return 0; 642 | } 643 | // TODO: Check padding values 644 | // By default encryption operations are padded using standard block padding 645 | // and the padding is checked and removed when decrypting. If the pad 646 | // parameter is zero then no padding is performed, the total amount of data 647 | // encrypted or decrypted must then be a multiple of the block size or an 648 | // error will occur. 649 | 650 | EVP_CIPHER_CTX *s_Ctx{EVP_CIPHER_CTX_new()}; 651 | int32_t s_Len{0}; 652 | int32_t s_PlaintextLen{0}; 653 | p_Output.resize(align_up(p_InputLen, static_cast(AES_BLOCK_SIZE)), '\0'); 654 | 655 | if (s_Ctx == nullptr) { 656 | LOG(ERROR) << "EVP_CIPHER_CTX_new"; 657 | goto fail; 658 | } 659 | 660 | if (p_KeyLen == 0x10) { 661 | if (EVP_DecryptInit_ex(s_Ctx, EVP_aes_128_cbc(), NULL, p_Key, p_Iv) != 1) { 662 | LOG(ERROR) << "EVP_DecryptInit_ex"; 663 | goto fail; 664 | } 665 | } else if (p_KeyLen == 0x18) { 666 | if (EVP_DecryptInit_ex(s_Ctx, EVP_aes_192_cbc(), NULL, p_Key, p_Iv) != 1) { 667 | LOG(ERROR) << "EVP_DecryptInit_ex"; 668 | goto fail; 669 | } 670 | } else if (p_KeyLen == 0x20) { 671 | if (EVP_DecryptInit_ex(s_Ctx, EVP_aes_256_cbc(), NULL, p_Key, p_Iv) != 1) { 672 | LOG(ERROR) << "EVP_DecryptInit_ex"; 673 | goto fail; 674 | } 675 | } else { 676 | LOG(ERROR) << "Unknown key size"; 677 | goto fail; 678 | } 679 | 680 | if (EVP_CIPHER_CTX_set_padding(s_Ctx, p_Padding) != 1) { 681 | LOG(ERROR) << "EVP_CIPHER_CTX_set_padding"; 682 | goto fail; 683 | } 684 | 685 | if (EVP_DecryptUpdate(s_Ctx, &p_Output[0], &s_Len, p_Input, p_InputLen) != 1) { 686 | LOG(ERROR) << "EVP_DecryptUpdate"; 687 | goto fail; 688 | } 689 | s_PlaintextLen = s_Len; 690 | 691 | if (EVP_DecryptFinal_ex(s_Ctx, &p_Output[s_Len], &s_Len) != 1) { 692 | LOG(ERROR) << "EVP_DecryptFinal_ex"; 693 | goto fail; 694 | } 695 | s_PlaintextLen += s_Len; 696 | 697 | EVP_CIPHER_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 698 | s_Ctx = nullptr; 699 | 700 | if (s_PlaintextLen < 0) { 701 | goto fail; 702 | } 703 | 704 | return s_PlaintextLen; 705 | 706 | fail: 707 | p_Output.clear(); 708 | p_Output.shrink_to_fit(); 709 | 710 | if (s_Ctx != nullptr) { 711 | EVP_CIPHER_CTX_free(s_Ctx); 712 | s_Ctx = nullptr; 713 | } 714 | 715 | return 0; 716 | } 717 | 718 | size_t AesEncryptCbcCts(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Iv, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 719 | if (p_Input == &p_Output[0]) { 720 | LOG(ERROR) << "Input is at the same location as output"; 721 | return 0; 722 | } 723 | 724 | p_Output.clear(); 725 | p_Output.shrink_to_fit(); 726 | 727 | if (p_Key == nullptr) { 728 | LOG(ERROR) << "Key data is NULL"; 729 | return 0; 730 | } 731 | if (p_KeyLen == 0) { 732 | LOG(ERROR) << "Key length is zero"; 733 | return 0; 734 | } 735 | if (p_Iv == nullptr) { 736 | LOG(ERROR) << "IV is NULL"; 737 | return 0; 738 | } 739 | if (p_Input == nullptr) { 740 | LOG(ERROR) << "Input data is NULL"; 741 | return 0; 742 | } 743 | if (p_InputLen == 0) { 744 | LOG(ERROR) << "Input length is zero"; 745 | return 0; 746 | } 747 | 748 | uint64_t s_NumDataLeft{p_InputLen}; 749 | uint64_t s_Offset{0}; 750 | std::vector s_TempBlock; 751 | std::vector s_TempIv; 752 | s_TempIv.insert(s_TempIv.begin(), &p_Iv[0], &p_Iv[0x10]); 753 | 754 | while (s_NumDataLeft >= AES_BLOCK_SIZE) { 755 | std::vector s_InputBlock; 756 | for (uint32_t i{0}; i < AES_BLOCK_SIZE; i++) { 757 | s_InputBlock.push_back(p_Input[s_Offset + i] ^ s_TempIv[i]); 758 | } 759 | if (AesEncryptEcb(p_Key, p_KeyLen, &s_InputBlock[0], AES_BLOCK_SIZE, s_TempBlock) != AES_BLOCK_SIZE) { 760 | LOG(ERROR) << "Error decrypting data"; 761 | goto fail; 762 | } 763 | p_Output.insert(p_Output.end(), s_TempBlock.begin(), s_TempBlock.end()); 764 | std::copy(s_TempBlock.begin(), s_TempBlock.begin() + s_TempIv.size(), s_TempIv.begin()); 765 | s_NumDataLeft -= AES_BLOCK_SIZE; 766 | s_Offset += AES_BLOCK_SIZE; 767 | } 768 | 769 | if (s_NumDataLeft > 0) { 770 | if (AesEncryptEcb(p_Key, p_KeyLen, &s_TempIv[0], AES_BLOCK_SIZE, s_TempBlock) != AES_BLOCK_SIZE) { 771 | LOG(ERROR) << "Error encrypting data"; 772 | goto fail; 773 | } 774 | 775 | for (uint32_t i{0}; i < s_NumDataLeft; i++) { 776 | p_Output.push_back(p_Input[s_Offset + i] ^ s_TempBlock[i]); 777 | } 778 | } 779 | 780 | return p_Output.size(); 781 | 782 | fail: 783 | p_Output.clear(); 784 | p_Output.shrink_to_fit(); 785 | 786 | return 0; 787 | } 788 | 789 | size_t AesDecryptCbcCts(const unsigned char *p_Key, size_t p_KeyLen, const unsigned char *p_Iv, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 790 | if (p_Input == &p_Output[0]) { 791 | LOG(ERROR) << "Input is at the same location as output"; 792 | return 0; 793 | } 794 | 795 | p_Output.clear(); 796 | p_Output.shrink_to_fit(); 797 | 798 | if (p_Key == nullptr) { 799 | LOG(ERROR) << "Key data is NULL"; 800 | return 0; 801 | } 802 | if (p_KeyLen == 0) { 803 | LOG(ERROR) << "Key length is zero"; 804 | return 0; 805 | } 806 | if (p_Iv == nullptr) { 807 | LOG(ERROR) << "IV is NULL"; 808 | return 0; 809 | } 810 | if (p_Input == nullptr) { 811 | LOG(ERROR) << "Input data is NULL"; 812 | return 0; 813 | } 814 | if (p_InputLen == 0) { 815 | LOG(ERROR) << "Input length is zero"; 816 | return 0; 817 | } 818 | 819 | uint64_t s_NumDataLeft{p_InputLen}; 820 | uint64_t s_Offset{0}; 821 | std::vector s_TempBlock; 822 | std::vector s_TempIv; 823 | s_TempIv.insert(s_TempIv.begin(), &p_Iv[0], &p_Iv[0x10]); 824 | 825 | while (s_NumDataLeft >= AES_BLOCK_SIZE) { 826 | if (AesDecryptEcb(p_Key, p_KeyLen, p_Input + s_Offset, AES_BLOCK_SIZE, s_TempBlock) != AES_BLOCK_SIZE) { 827 | LOG(ERROR) << "Error decrypting data"; 828 | goto fail; 829 | } 830 | for (uint32_t i{0}; i < AES_BLOCK_SIZE; i++) { 831 | p_Output.push_back(s_TempBlock[i] ^ s_TempIv[i]); 832 | } 833 | std::copy(p_Input + s_Offset, p_Input + s_Offset + s_TempIv.size(), s_TempIv.begin()); 834 | s_NumDataLeft -= AES_BLOCK_SIZE; 835 | s_Offset += AES_BLOCK_SIZE; 836 | } 837 | 838 | if (s_NumDataLeft > 0) { 839 | if (AesEncryptEcb(p_Key, p_KeyLen, &s_TempIv[0], AES_BLOCK_SIZE, s_TempBlock) != AES_BLOCK_SIZE) { 840 | LOG(ERROR) << "Error encrypting data"; 841 | goto fail; 842 | } 843 | 844 | for (uint32_t i{0}; i < s_NumDataLeft; i++) { 845 | p_Output.push_back(p_Input[s_Offset + i] ^ s_TempBlock[i]); 846 | } 847 | } 848 | 849 | return p_Output.size(); 850 | 851 | fail: 852 | p_Output.clear(); 853 | p_Output.shrink_to_fit(); 854 | 855 | return 0; 856 | } 857 | 858 | bool Sha256(const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 859 | if (p_Input == &p_Output[0]) { 860 | LOG(ERROR) << "Input is at the same location as output"; 861 | return false; 862 | } 863 | 864 | p_Output.clear(); 865 | p_Output.shrink_to_fit(); 866 | 867 | if (p_Input == nullptr) { 868 | LOG(ERROR) << "Input data is NULL"; 869 | return false; 870 | } 871 | if (p_InputLen == 0) { 872 | LOG(ERROR) << "Input length is zero"; 873 | return false; 874 | } 875 | 876 | EVP_MD_CTX *s_Ctx{EVP_MD_CTX_new()}; 877 | unsigned int s_DigestLen{SHA256_DIGEST_LENGTH}; 878 | p_Output.resize(s_DigestLen, '\0'); 879 | 880 | if (s_Ctx == nullptr) { 881 | LOG(ERROR) << "EVP_MD_CTX_new"; 882 | goto fail; 883 | } 884 | 885 | if (EVP_DigestInit_ex(s_Ctx, EVP_sha256(), NULL) != 1) { 886 | LOG(ERROR) << "EVP_DigestInit_ex"; 887 | goto fail; 888 | } 889 | 890 | if (EVP_DigestUpdate(s_Ctx, p_Input, p_InputLen) != 1) { 891 | LOG(ERROR) << "EVP_DigestUpdate"; 892 | goto fail; 893 | } 894 | 895 | if (EVP_DigestFinal_ex(s_Ctx, &p_Output[0], &s_DigestLen) != 1) { 896 | LOG(ERROR) << "EVP_DigestFinal_ex"; 897 | goto fail; 898 | } 899 | 900 | EVP_MD_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 901 | s_Ctx = nullptr; 902 | 903 | return true; 904 | 905 | fail: 906 | p_Output.clear(); 907 | p_Output.shrink_to_fit(); 908 | 909 | if (s_Ctx != nullptr) { 910 | EVP_MD_CTX_free(s_Ctx); 911 | s_Ctx = nullptr; 912 | } 913 | 914 | return false; 915 | } 916 | 917 | bool HmacSha1(const unsigned char *p_Key, int32_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 918 | if (p_Input == &p_Output[0]) { 919 | LOG(ERROR) << "Input is at the same location as output"; 920 | return false; 921 | } 922 | 923 | p_Output.clear(); 924 | p_Output.shrink_to_fit(); 925 | 926 | if (p_Key == nullptr) { 927 | LOG(ERROR) << "Input data is NULL"; 928 | return false; 929 | } 930 | if (p_KeyLen <= 0) { 931 | LOG(ERROR) << "Input length is zero"; 932 | return false; 933 | } 934 | if (p_Input == nullptr) { 935 | LOG(ERROR) << "Input data is NULL"; 936 | return false; 937 | } 938 | if (p_InputLen == 0) { 939 | LOG(ERROR) << "Input length is zero"; 940 | return false; 941 | } 942 | 943 | EVP_MD_CTX *s_Ctx{EVP_MD_CTX_new()}; 944 | EVP_PKEY *s_Pkey{EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, p_Key, p_KeyLen)}; 945 | size_t s_DigestLen{SHA_DIGEST_LENGTH}; 946 | p_Output.resize(s_DigestLen, '\0'); 947 | 948 | if (s_Ctx == nullptr) { 949 | LOG(ERROR) << "EVP_MD_CTX_new"; 950 | goto fail; 951 | } 952 | if (s_Pkey == nullptr) { 953 | LOG(ERROR) << "EVP_PKEY_new_mac_key"; 954 | goto fail; 955 | } 956 | 957 | if (EVP_DigestSignInit(s_Ctx, NULL, EVP_sha1(), NULL, s_Pkey) != 1) { 958 | LOG(ERROR) << "EVP_DigestSignInit"; 959 | goto fail; 960 | } 961 | 962 | if (EVP_DigestSignUpdate(s_Ctx, p_Input, p_InputLen) != 1) { 963 | LOG(ERROR) << "EVP_DigestSignUpdate"; 964 | goto fail; 965 | } 966 | 967 | if (EVP_DigestSignFinal(s_Ctx, &p_Output[0], &s_DigestLen) != 1) { 968 | LOG(ERROR) << "EVP_DigestSignFinal"; 969 | goto fail; 970 | } 971 | 972 | EVP_MD_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 973 | s_Ctx = nullptr; 974 | EVP_PKEY_free(s_Pkey); // Does not need `if (s_Pkey != nullptr)` as it's checked against nullptr above 975 | s_Pkey = nullptr; 976 | 977 | return true; 978 | 979 | fail: 980 | p_Output.clear(); 981 | p_Output.shrink_to_fit(); 982 | 983 | if (s_Ctx != nullptr) { 984 | EVP_MD_CTX_free(s_Ctx); 985 | s_Ctx = nullptr; 986 | } 987 | if (s_Pkey != nullptr) { 988 | EVP_PKEY_free(s_Pkey); 989 | s_Pkey = nullptr; 990 | } 991 | 992 | return false; 993 | } 994 | 995 | bool HmacSha256(const unsigned char *p_Key, int32_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 996 | if (p_Input == &p_Output[0]) { 997 | LOG(ERROR) << "Input is at the same location as output"; 998 | return false; 999 | } 1000 | 1001 | p_Output.clear(); 1002 | p_Output.shrink_to_fit(); 1003 | 1004 | if (p_Key == nullptr) { 1005 | LOG(ERROR) << "Input data is NULL"; 1006 | return false; 1007 | } 1008 | if (p_KeyLen <= 0) { 1009 | LOG(ERROR) << "Input length is zero"; 1010 | return false; 1011 | } 1012 | if (p_Input == nullptr) { 1013 | LOG(ERROR) << "Input data is NULL"; 1014 | return false; 1015 | } 1016 | if (p_InputLen == 0) { 1017 | LOG(ERROR) << "Input length is zero"; 1018 | return false; 1019 | } 1020 | 1021 | EVP_MD_CTX *s_Ctx{EVP_MD_CTX_new()}; 1022 | EVP_PKEY *s_Pkey{EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, p_Key, p_KeyLen)}; 1023 | size_t s_DigestLen{SHA256_DIGEST_LENGTH}; 1024 | p_Output.resize(s_DigestLen, '\0'); 1025 | 1026 | if (s_Ctx == nullptr) { 1027 | LOG(ERROR) << "EVP_MD_CTX_new"; 1028 | goto fail; 1029 | } 1030 | if (s_Pkey == nullptr) { 1031 | LOG(ERROR) << "EVP_PKEY_new_mac_key"; 1032 | goto fail; 1033 | } 1034 | 1035 | if (EVP_DigestSignInit(s_Ctx, NULL, EVP_sha256(), NULL, s_Pkey) != 1) { 1036 | LOG(ERROR) << "EVP_DigestSignInit"; 1037 | goto fail; 1038 | } 1039 | 1040 | if (EVP_DigestSignUpdate(s_Ctx, p_Input, p_InputLen) != 1) { 1041 | LOG(ERROR) << "EVP_DigestSignUpdate"; 1042 | goto fail; 1043 | } 1044 | 1045 | if (EVP_DigestSignFinal(s_Ctx, &p_Output[0], &s_DigestLen) != 1) { 1046 | LOG(ERROR) << "EVP_DigestSignFinal"; 1047 | goto fail; 1048 | } 1049 | 1050 | EVP_MD_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 1051 | s_Ctx = nullptr; 1052 | EVP_PKEY_free(s_Pkey); // Does not need `if (s_Pkey != nullptr)` as it's checked against nullptr above 1053 | s_Pkey = nullptr; 1054 | 1055 | return true; 1056 | 1057 | fail: 1058 | p_Output.clear(); 1059 | p_Output.shrink_to_fit(); 1060 | 1061 | if (s_Ctx != nullptr) { 1062 | EVP_MD_CTX_free(s_Ctx); 1063 | s_Ctx = nullptr; 1064 | } 1065 | if (s_Pkey != nullptr) { 1066 | EVP_PKEY_free(s_Pkey); 1067 | s_Pkey = nullptr; 1068 | } 1069 | 1070 | return false; 1071 | } 1072 | 1073 | bool Cmac(const unsigned char *p_Key, int32_t p_KeyLen, const unsigned char *p_Input, size_t p_InputLen, std::vector &p_Output) { 1074 | // TODO: Can use EVP_MAC? 1075 | if (p_Input == &p_Output[0]) { 1076 | LOG(ERROR) << "Input is at the same location as output"; 1077 | return false; 1078 | } 1079 | 1080 | p_Output.clear(); 1081 | p_Output.shrink_to_fit(); 1082 | 1083 | if (p_Key == nullptr) { 1084 | LOG(ERROR) << "Input data is NULL"; 1085 | return false; 1086 | } 1087 | if (p_KeyLen <= 0) { 1088 | LOG(ERROR) << "Input length is zero"; 1089 | return false; 1090 | } 1091 | if (p_Input == nullptr) { 1092 | LOG(ERROR) << "Input data is NULL"; 1093 | return false; 1094 | } 1095 | if (p_InputLen == 0) { 1096 | LOG(ERROR) << "Input length is zero"; 1097 | return false; 1098 | } 1099 | 1100 | EVP_MD_CTX *s_Ctx{EVP_MD_CTX_new()}; 1101 | EVP_PKEY_CTX *s_Kctx{EVP_PKEY_CTX_new_id(EVP_PKEY_CMAC, NULL)}; 1102 | EVP_PKEY *s_Pkey{nullptr}; 1103 | size_t s_DigestLen{static_cast(p_KeyLen)}; 1104 | p_Output.resize(0x10, '\0'); // The static value `0x10` below is the MAC length per rfc4493 1105 | 1106 | if (s_Ctx == nullptr) { 1107 | LOG(ERROR) << "EVP_MD_CTX_new"; 1108 | goto fail; 1109 | } 1110 | if (s_Kctx == nullptr) { 1111 | LOG(ERROR) << "EVP_PKEY_CTX_new_id"; 1112 | goto fail; 1113 | } 1114 | 1115 | if (EVP_PKEY_keygen_init(s_Kctx) != 1) { 1116 | LOG(ERROR) << "EVP_PKEY_keygen_init"; 1117 | goto fail; 1118 | } 1119 | 1120 | if (p_KeyLen == 0x10) { 1121 | if (EVP_PKEY_CTX_ctrl(s_Kctx, -1, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_CIPHER, 0, const_cast(EVP_aes_128_cbc())) <= 0) { 1122 | LOG(ERROR) << "EVP_PKEY_CTX_ctrl"; 1123 | goto fail; 1124 | } 1125 | } else if (p_KeyLen == 0x18) { 1126 | if (EVP_PKEY_CTX_ctrl(s_Kctx, -1, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_CIPHER, 0, const_cast(EVP_aes_192_cbc())) <= 0) { 1127 | LOG(ERROR) << "EVP_PKEY_CTX_ctrl"; 1128 | goto fail; 1129 | } 1130 | } else if (p_KeyLen == 0x20) { 1131 | if (EVP_PKEY_CTX_ctrl(s_Kctx, -1, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_CIPHER, 0, const_cast(EVP_aes_256_cbc())) <= 0) { 1132 | LOG(ERROR) << "EVP_PKEY_CTX_ctrl"; 1133 | goto fail; 1134 | } 1135 | } else { 1136 | LOG(ERROR) << "Unknown key size"; 1137 | goto fail; 1138 | } 1139 | 1140 | if (EVP_PKEY_CTX_ctrl(s_Kctx, -1, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_SET_MAC_KEY, p_KeyLen, const_cast(p_Key)) <= 0) { 1141 | LOG(ERROR) << "EVP_PKEY_CTX_ctrl"; 1142 | goto fail; 1143 | } 1144 | 1145 | if (EVP_PKEY_keygen(s_Kctx, &s_Pkey) != 1) { 1146 | LOG(ERROR) << "EVP_PKEY_keygen"; 1147 | goto fail; 1148 | } 1149 | 1150 | if (s_Pkey == nullptr) { 1151 | LOG(ERROR) << "EVP_PKEY_keygen"; 1152 | goto fail; 1153 | } 1154 | 1155 | if (EVP_DigestSignInit(s_Ctx, NULL, NULL, NULL, s_Pkey) != 1) { 1156 | LOG(ERROR) << "EVP_DigestSignInit"; 1157 | goto fail; 1158 | } 1159 | 1160 | if (EVP_DigestSignUpdate(s_Ctx, p_Input, p_InputLen) != 1) { 1161 | LOG(ERROR) << "EVP_DigestSignUpdate"; 1162 | goto fail; 1163 | } 1164 | 1165 | if (EVP_DigestSignFinal(s_Ctx, &p_Output[0], &s_DigestLen) != 1) { 1166 | LOG(ERROR) << "EVP_DigestSignFinal"; 1167 | goto fail; 1168 | } 1169 | 1170 | EVP_MD_CTX_free(s_Ctx); // Does not need `if (s_Ctx != nullptr)` as it's checked against nullptr above 1171 | s_Ctx = nullptr; 1172 | EVP_PKEY_CTX_free(s_Kctx); // Does not need `if (s_Kctx != nullptr)` as it's checked against nullptr above 1173 | s_Kctx = nullptr; 1174 | EVP_PKEY_free(s_Pkey); // Does not need `if (s_Pkey != nullptr)` as it's checked against nullptr above 1175 | s_Pkey = nullptr; 1176 | 1177 | return true; 1178 | 1179 | fail: 1180 | p_Output.clear(); 1181 | p_Output.shrink_to_fit(); 1182 | 1183 | if (s_Ctx != nullptr) { 1184 | EVP_MD_CTX_free(s_Ctx); 1185 | s_Ctx = nullptr; 1186 | } 1187 | if (s_Kctx != nullptr) { 1188 | EVP_PKEY_CTX_free(s_Kctx); 1189 | s_Kctx = nullptr; 1190 | } 1191 | if (s_Pkey != nullptr) { 1192 | EVP_PKEY_free(s_Pkey); 1193 | s_Pkey = nullptr; 1194 | } 1195 | 1196 | return false; 1197 | } 1198 | --------------------------------------------------------------------------------