Merge branch 'master' into shader-decomp

This commit is contained in:
wheremyfoodat 2024-07-31 23:44:22 +03:00
commit ffcf352dd2
5 changed files with 156 additions and 57 deletions

View file

@ -1,13 +1,15 @@
#include <iostream>
#include <fstream>
#include "crypto/aes_engine.hpp"
#include <fstream>
#include <iostream>
#include <tuple>
#include "helpers.hpp"
namespace Crypto {
void AESEngine::loadKeys(const std::filesystem::path& path) {
std::ifstream file(path, std::ios::in);
if (file.fail()) {
Helpers::warn("Keys: Couldn't read key file: %s", path.c_str());
return;
@ -58,18 +60,10 @@ namespace Crypto {
}
switch (keyType) {
case 'X':
setKeyX(slotId, key.value());
break;
case 'Y':
setKeyY(slotId, key.value());
break;
case 'N':
setNormalKey(slotId, key.value());
break;
default:
Helpers::warn("Keys: Invalid key type %c", keyType);
break;
case 'X': setKeyX(slotId, key.value()); break;
case 'Y': setKeyY(slotId, key.value()); break;
case 'N': setNormalKey(slotId, key.value()); break;
default: Helpers::warn("Keys: Invalid key type %c", keyType); break;
}
}
@ -80,4 +74,65 @@ namespace Crypto {
keysLoaded = true;
}
};
void AESEngine::setSeedPath(const std::filesystem::path& path) { seedDatabase.open(path, "rb"); }
// Loads seeds from a seed file, return true on success and false on failure
bool AESEngine::loadSeeds() {
if (!seedDatabase.isOpen()) {
return false;
}
// The # of seeds is stored at offset 0
u32_le seedCount = 0;
if (!seedDatabase.rewind()) {
return false;
}
auto [success, size] = seedDatabase.readBytes(&seedCount, sizeof(u32));
if (!success || size != sizeof(u32)) {
return false;
}
// Key data starts from offset 16
if (!seedDatabase.seek(16)) {
return false;
}
Crypto::Seed seed;
for (uint i = 0; i < seedCount; i++) {
std::tie(success, size) = seedDatabase.readBytes(&seed, sizeof(seed));
if (!success || size != sizeof(seed)) {
return false;
}
seeds.push_back(seed);
}
return true;
}
std::optional<Crypto::AESKey> AESEngine::getSeedFromDB(u64 titleID) {
// We don't have a seed db nor any seeds loaded, return nullopt
if (!seedDatabase.isOpen() && seeds.empty()) {
return std::nullopt;
}
// We have a seed DB but haven't loaded the seeds yet, so load them
if (seedDatabase.isOpen() && seeds.empty()) {
bool success = loadSeeds();
if (!success) {
return std::nullopt;
}
}
for (Crypto::Seed& seed : seeds) {
if (seed.titleID == titleID) {
return seed.seed;
}
}
return std::nullopt;
}
}; // namespace Crypto

View file

@ -1,12 +1,15 @@
#include "loader/ncch.hpp"
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <cstring>
#include <vector>
#include "loader/lz77.hpp"
#include "loader/ncch.hpp"
#include "memory.hpp"
#include <cryptopp/sha.h>
#include <cstring>
#include <iostream>
#include <vector>
#include "loader/lz77.hpp"
#include "memory.hpp"
bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSInfo &info) {
// 0x200 bytes for the NCCH header
@ -70,8 +73,26 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn
if (!seedCrypto) {
secondaryKeyY = primaryKeyY;
} else {
Helpers::warn("Seed crypto is not supported");
gotCryptoKeys = false;
// In seed crypto mode, the secondary key is computed through a SHA256 hash of the primary key and a title-specific seed, which we fetch
// from seeddb.bin
std::optional<Crypto::AESKey> seedOptional = aesEngine.getSeedFromDB(programID);
if (seedOptional.has_value()) {
auto seed = *seedOptional;
CryptoPP::SHA256 shaEngine;
std::array<u8, 32> data;
std::array<u8, CryptoPP::SHA256::DIGESTSIZE> hash;
std::memcpy(&data[0], primaryKeyY.data(), primaryKeyY.size());
std::memcpy(&data[16], seed.data(), seed.size());
shaEngine.CalculateDigest(hash.data(), data.data(), data.size());
// Note that SHA256 will produce a 256-bit hash, while we only need 128 bits cause this is an AES key
// So the latter 16 bytes of the SHA256 are thrown out.
std::memcpy(secondaryKeyY.data(), hash.data(), secondaryKeyY.size());
} else {
Helpers::warn("Couldn't find a seed value for this title. Make sure you have a seeddb.bin file alongside your aes_keys.txt");
gotCryptoKeys = false;
}
}
auto primaryResult = getPrimaryKey(aesEngine, primaryKeyY);

View file

@ -220,6 +220,8 @@ bool Emulator::loadROM(const std::filesystem::path& path) {
const std::filesystem::path appDataPath = getAppDataRoot();
const std::filesystem::path dataPath = appDataPath / path.filename().stem();
const std::filesystem::path aesKeysPath = appDataPath / "sysdata" / "aes_keys.txt";
const std::filesystem::path seedDBPath = appDataPath / "sysdata" / "seeddb.bin";
IOFile::setAppDataDir(dataPath);
// 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
@ -229,6 +231,10 @@ bool Emulator::loadROM(const std::filesystem::path& path) {
aesEngine.loadKeys(aesKeysPath);
}
if (std::filesystem::exists(seedDBPath, ec) && !ec) {
aesEngine.setSeedPath(seedDBPath);
}
kernel.initializeFS();
auto extension = path.extension();
bool success; // Tracks if we loaded the ROM successfully