From 3cf84276705299370556af96ba3d9d619863bac4 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 27 Jun 2023 01:12:17 +0300 Subject: [PATCH] [Crypto] Slightly more robust error handling --- include/crypto/aes_engine.hpp | 38 ++++++++++++++++------------------ src/core/crypto/aes_engine.cpp | 17 +++++++-------- src/core/loader/ncch.cpp | 8 +++++++ src/emulator.cpp | 10 +++++---- src/main.cpp | 2 +- 5 files changed, 41 insertions(+), 34 deletions(-) diff --git a/include/crypto/aes_engine.hpp b/include/crypto/aes_engine.hpp index 0bb579c5..3cb75bac 100644 --- a/include/crypto/aes_engine.hpp +++ b/include/crypto/aes_engine.hpp @@ -11,11 +11,10 @@ namespace Crypto { constexpr std::size_t AesKeySize = 0x10; + using AESKey = std::array; - using AESKey = std::array; - - template - static std::array rolArray(const std::array& value, std::size_t bits) { + template + static std::array rolArray(const std::array& value, std::size_t bits) { const auto bitWidth = N * CHAR_BIT; bits %= bitWidth; @@ -23,7 +22,7 @@ namespace Crypto { const auto byteShift = bits / CHAR_BIT; const auto bitShift = bits % CHAR_BIT; - std::array result; + std::array result; for (std::size_t i = 0; i < N; i++) { result[i] = ((value[(i + byteShift) % N] << bitShift) | (value[(i + byteShift + 1) % N] >> (CHAR_BIT - bitShift))) & UINT8_MAX; @@ -32,24 +31,24 @@ namespace Crypto { return result; } - template - static std::array addArray(const std::array& a, const std::array& b) { - std::array result; + template + static std::array addArray(const std::array& a, const std::array& b) { + std::array result; std::size_t sum = 0; std::size_t carry = 0; for (std::int64_t i = N - 1; i >= 0; i--) { sum = a[i] + b[i] + carry; carry = sum >> CHAR_BIT; - result[i] = static_cast(sum & UINT8_MAX); + result[i] = static_cast(sum & UINT8_MAX); } return result; } - template - static std::array xorArray(const std::array& a, const std::array& b) { - std::array result; + template + static std::array xorArray(const std::array& a, const std::array& b) { + std::array result; for (std::size_t i = 0; i < N; i++) { result[i] = a[i] ^ b[i]; @@ -65,16 +64,16 @@ namespace Crypto { AESKey rawKey; for (std::size_t i = 0; i < rawKey.size(); i++) { - rawKey[i] = static_cast(std::stoi(hex.substr(i * 2, 2), 0, 16)); + rawKey[i] = static_cast(std::stoi(hex.substr(i * 2, 2), 0, 16)); } return rawKey; } struct AESKeySlot { - std::optional keyX{std::nullopt}; - std::optional keyY{std::nullopt}; - std::optional normalKey{std::nullopt}; + std::optional keyX = std::nullopt; + std::optional keyY = std::nullopt; + std::optional normalKey = std::nullopt; }; enum KeySlotId : std::size_t { @@ -88,8 +87,9 @@ namespace Crypto { private: constexpr static std::size_t AesKeySlotCount = 0x40; - std::optional m_generator; + std::optional m_generator = std::nullopt; std::array m_slots; + bool keysLoaded = false; constexpr void updateNormalKey(std::size_t slotId) { if (m_generator.has_value() && hasKeyX(slotId) && hasKeyY(slotId)) { @@ -103,8 +103,8 @@ namespace Crypto { public: AESEngine() {} - void loadKeys(const std::filesystem::path& path); + bool haveKeys() { return keysLoaded; } constexpr bool hasKeyX(std::size_t slotId) { if (slotId >= AesKeySlotCount) { @@ -121,7 +121,6 @@ namespace Crypto { constexpr void setKeyX(std::size_t slotId, const AESKey &key) { if (slotId < AesKeySlotCount) { m_slots.at(slotId).keyX = key; - updateNormalKey(slotId); } } @@ -141,7 +140,6 @@ namespace Crypto { constexpr void setKeyY(std::size_t slotId, const AESKey &key) { if (slotId < AesKeySlotCount) { m_slots.at(slotId).keyY = key; - updateNormalKey(slotId); } } diff --git a/src/core/crypto/aes_engine.cpp b/src/core/crypto/aes_engine.cpp index 53694d02..f4bf3494 100644 --- a/src/core/crypto/aes_engine.cpp +++ b/src/core/crypto/aes_engine.cpp @@ -9,13 +9,12 @@ namespace Crypto { std::ifstream file(path, std::ios::in); if (file.fail()) { - Helpers::warn("keys: Couldn't read key file: %s", path.c_str()); + Helpers::warn("Keys: Couldn't read key file: %s", path.c_str()); return; } while (!file.eof()) { std::string line; - std::getline(file, line); // Skip obvious invalid lines @@ -24,9 +23,8 @@ namespace Crypto { } const auto parts = Helpers::split(line, '='); - if (parts.size() != 2) { - Helpers::warn("keys: Failed to parse %s", line.c_str()); + Helpers::warn("Keys: Failed to parse %s", line.c_str()); continue; } @@ -37,16 +35,15 @@ namespace Crypto { char keyType; bool is_generator = name == "generator"; - if (!is_generator && std::sscanf(name.c_str(), "slot0x%zXKey%c", &slotId, &keyType) != 2) { - Helpers::warn("keys: Ignoring unknown key %s", name.c_str()); + Helpers::warn("Keys: Ignoring unknown key %s", name.c_str()); continue; } auto key = createKeyFromHex(rawKeyHex); if (!key.has_value()) { - Helpers::warn("keys: Failed to parse raw key %s", rawKeyHex.c_str()); + Helpers::warn("Keys: Failed to parse raw key %s", rawKeyHex.c_str()); continue; } @@ -56,7 +53,7 @@ namespace Crypto { } if (slotId >= AesKeySlotCount) { - Helpers::warn("keys: Invalid key slot id %u", slotId); + Helpers::warn("Keys: Invalid key slot id %u", slotId); continue; } @@ -71,7 +68,7 @@ namespace Crypto { setNormalKey(slotId, key.value()); break; default: - Helpers::warn("keys: Invalid key type %c", keyType); + Helpers::warn("Keys: Invalid key type %c", keyType); break; } } @@ -80,5 +77,7 @@ namespace Crypto { for (std::size_t i = 0; i < AesKeySlotCount; i++) { updateNormalKey(i); } + + keysLoaded = true; } }; \ No newline at end of file diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 1a4178fe..0f29ddb5 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -54,6 +54,14 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn romFS.hashRegionSize = u64(*(u32*)&header[0x1B8]) * mediaUnit; if (encrypted) { + if (!aesEngine.haveKeys()) { + Helpers::panic( + "Loaded an encrypted ROM but AES keys don't seem to have been provided correctly! Navigate to the emulator's\n" + "app data folder and make sure you have a sysdata directory with a file called aes_keys.txt which contains your keys!" + ); + return false; + } + Crypto::AESKey primaryKeyY; Crypto::AESKey secondaryKeyY; std::memcpy(primaryKeyY.data(), header, primaryKeyY.size()); diff --git a/src/emulator.cpp b/src/emulator.cpp index f37e8825..d41c6928 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -137,15 +137,17 @@ bool Emulator::loadROM(const std::filesystem::path& path) { char* appData = SDL_GetPrefPath(nullptr, "Alber"); const std::filesystem::path appDataPath = std::filesystem::path(appData); const std::filesystem::path dataPath = appDataPath / path.filename().stem(); - const std::filesystem::path aesKeysPath = appDataPath / "sysdata" / "aes_keys.txt"; + const std::filesystem::path aesKeysPath = appDataPath / "sysdata" / "aes_keys.txt"; IOFile::setAppDataDir(dataPath); + SDL_free(appData); - if (std::filesystem::exists(aesKeysPath)) { + // Open the text file containing our AES keys if it exists. We use the std::filesystem::exists overload that takes an error code param to + // avoid the call throwing exceptions + std::error_code ec; + if (std::filesystem::exists(aesKeysPath, ec) && !ec) { aesEngine.loadKeys(aesKeysPath); } - SDL_free(appData); - kernel.initializeFS(); auto extension = path.extension(); diff --git a/src/main.cpp b/src/main.cpp index 39f6403c..d78dc407 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,7 @@ int main (int argc, char *argv[]) { emu.initGraphicsContext(); - auto romPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "Metroid Prime - Federation Force (Europe) (En,Fr,De,Es,It).3ds"); + auto romPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "OoT Demo Encrypted.3ds"); if (!emu.loadROM(romPath)) { // For some reason just .c_str() doesn't show the proper path Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str());