feat: crypto: Add basic AES keyslot manager

We loads keys from AppData/Alber/sysdata/aes_keys.txt.

NOTE: We do differ from other emulators by not hardcoding the generator key, it's the user responsibility to provide it in aes_keys.txt.
This commit is contained in:
Mary 2023-06-19 23:13:20 +02:00
parent bf85b405af
commit 2e5bc0cb14
6 changed files with 280 additions and 2 deletions

View file

@ -0,0 +1,167 @@
#pragma once
#include <array>
#include <cstring>
#include <cstdint>
#include <climits>
#include <filesystem>
#include <optional>
#include "helpers.hpp"
namespace Crypto {
constexpr std::size_t AesKeySize = 0x10;
using AESKey = std::array<uint8_t, AesKeySize>;
template<std::size_t N>
static std::array<uint8_t, N> rolArray(const std::array<uint8_t, N>& value, std::size_t bits) {
const auto bitWidth = N * UINT8_WIDTH;
bits %= bitWidth;
const auto byteShift = bits / UINT8_WIDTH;
const auto bitShift = bits % UINT8_WIDTH;
std::array<uint8_t, N> result;
for (std::size_t i = 0; i < N; i++) {
result[i] = ((value[(i + byteShift) % N] << bitShift) | (value[(i + byteShift + 1) % N] >> (UINT8_WIDTH - bitShift))) & UINT8_MAX;
}
return result;
}
template<std::size_t N>
static std::array<uint8_t, N> addArray(const std::array<uint8_t, N>& a, const std::array<uint8_t, N>& b) {
std::array<uint8_t, N> 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 >> UINT8_WIDTH;
result[i] = static_cast<std::uint8_t>(sum & UINT8_MAX);
}
return result;
}
template<std::size_t N>
static std::array<uint8_t, N> xorArray(const std::array<uint8_t, N>& a, const std::array<uint8_t, N>& b) {
std::array<uint8_t, N> result;
for (std::size_t i = 0; i < N; i++) {
result[i] = a[i] ^ b[i];
}
return result;
}
static std::optional<AESKey> createKeyFromHex(const std::string& hex) {
if (hex.size() < 32) {
return {};
}
AESKey rawKey;
for (std::size_t i = 0; i < rawKey.size(); i++) {
rawKey[i] = static_cast<uint8_t>(std::stoi(hex.substr(i * 2, 2), 0, 16));
}
return rawKey;
}
struct AESKeySlot {
std::optional<AESKey> keyX;
std::optional<AESKey> keyY;
std::optional<AESKey> normalKey;
};
enum KeySlotId : std::size_t {
NCCHKey0 = 0x2C,
NCCHKey1 = 0x25,
NCCHKey2 = 0x18,
NCCHKey3 = 0x1B,
};
class AESEngine {
private:
constexpr static std::size_t AesKeySlotCount = 0x40;
std::optional<AESKey> m_generator;
std::array<AESKeySlot, AesKeySlotCount> m_slots;
constexpr void updateNormalKey(std::size_t slotId) {
if (m_generator.has_value() && hasKeyX(slotId) && hasKeyY(slotId)) {
auto &keySlot = m_slots.at(slotId);
AESKey keyX = keySlot.keyX.value();
AESKey keyY = keySlot.keyY.value();
keySlot.normalKey = rolArray(addArray(xorArray(rolArray(keyX, 2), keyY), m_generator.value()), 87);
}
}
public:
AESEngine() {}
void loadKeys(const std::filesystem::path& path);
constexpr bool hasKeyX(std::size_t slotId) {
if (slotId >= AesKeySlotCount) {
return false;
}
return m_slots.at(slotId).keyX.has_value();
}
constexpr AESKey getKeyX(std::size_t slotId) {
return m_slots.at(slotId).keyX.value_or(AESKey{});
}
constexpr void setKeyX(std::size_t slotId, const AESKey &key) {
if (slotId < AesKeySlotCount) {
m_slots.at(slotId).keyX = key;
updateNormalKey(slotId);
}
}
constexpr bool hasKeyY(std::size_t slotId) {
if (slotId >= AesKeySlotCount) {
return false;
}
return m_slots.at(slotId).keyY.has_value();
}
constexpr AESKey getKeyY(std::size_t slotId) {
return m_slots.at(slotId).keyY.value_or(AESKey{});
}
constexpr void setKeyY(std::size_t slotId, const AESKey &key) {
if (slotId < AesKeySlotCount) {
m_slots.at(slotId).keyY = key;
updateNormalKey(slotId);
}
}
constexpr bool hasNormalKey(std::size_t slotId) {
if (slotId >= AesKeySlotCount) {
return false;
}
return m_slots.at(slotId).normalKey.has_value();
}
constexpr AESKey getNormalKey(std::size_t slotId) {
return m_slots.at(slotId).normalKey.value_or(AESKey{});
}
constexpr void setNormalKey(std::size_t slotId, const AESKey &key) {
if (slotId < AesKeySlotCount) {
m_slots.at(slotId).normalKey = key;
}
}
};
}

View file

@ -6,6 +6,7 @@
#include <glad/gl.h>
#include "cpu.hpp"
#include "crypto/aes_engine.hpp"
#include "io_file.hpp"
#include "memory.hpp"
#include "opengl.hpp"
@ -20,6 +21,7 @@ class Emulator {
GPU gpu;
Memory memory;
Kernel kernel;
Crypto::AESEngine aesEngine;
SDL_Window* window;
SDL_GLContext glContext;

View file

@ -5,6 +5,8 @@
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
@ -164,6 +166,19 @@ namespace Helpers {
return std::bit_cast<To, From>(from);
}
#endif
static std::vector<std::string> split(const std::string& s, const char c) {
std::istringstream tmp(s);
std::vector<std::string> result(1);
while (std::getline(tmp, *result.rbegin(), c)) {
result.emplace_back();
}
// Remove temporary slot
result.pop_back();
return result;
}
}; // namespace Helpers
// UDLs for memory size values