melonDS/src/DSi_AES.cpp

578 lines
14 KiB
C++
Raw Normal View History

2019-06-18 17:00:44 +00:00
/*
2023-11-03 23:21:46 +00:00
Copyright 2016-2023 melonDS team
2019-06-18 17:00:44 +00:00
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "DSi_NAND.h"
#include "DSi_AES.h"
#include "Platform.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
#define _printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); }
#define _printhex2(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); }
#define _printhexR(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); printf("\n"); }
#define _printhex2R(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); }
DSi_AES::DSi_AES(melonDS::DSi& dsi) : DSi(dsi)
{
const u8 zero[16] = {0};
AES_init_ctx_iv(&Ctx, zero, zero);
}
2023-11-04 16:46:52 +00:00
DSi_AES::~DSi_AES()
{
}
2023-11-04 16:46:52 +00:00
void DSi_AES::Reset()
{
Cnt = 0;
BlkCnt = 0;
RemExtra = 0;
RemBlocks = 0;
OutputFlush = false;
InputDMASize = 0;
OutputDMASize = 0;
AESMode = 0;
2020-12-30 22:37:46 +00:00
InputFIFO.Clear();
OutputFIFO.Clear();
memset(IV, 0, sizeof(IV));
memset(MAC, 0, sizeof(MAC));
memset(KeyNormal, 0, sizeof(KeyNormal));
memset(KeyX, 0, sizeof(KeyX));
memset(KeyY, 0, sizeof(KeyY));
memset(CurKey, 0, sizeof(CurKey));
memset(CurMAC, 0, sizeof(CurMAC));
memset(OutputMAC, 0, sizeof(OutputMAC));
OutputMACDue = false;
// initialize keys
Refactor how save data (including SD cards) is initialized (#1898) * Remove `FATStorage::Open` and `FATStorage::Close` - That's what the constructor and destructor are for, respectively * Add `FATStorage::IsReadOnly` * Slight cleanup of `FATStorage` - Make it move-constructible and move-assignable - Represent the absence of a sync directory with `std::optional`, not an empty string - Add `FATStorageArgs` for later use * Refactor `CartHomebrew` to accept an optional `FATStorageArgs` - `CartHomebrew` uses it to load an SD card image - Not passing a `FATStorage` directly because we won't know if we need to load the card until we parse the ROM - Store the `FATStorage` inside a `std::optional` instead of a pointer - `CartHomebrew::Reset` no longer reloads the SD card; the frontend needs to set it with the `SetSDCard` method * Close `NANDImage::CurFile` when move-assigning - Whoops * Add `Args.h` - To construct a `NDS` or `DSi` with arguments - Mostly intended for system files * Fix incorrect `final` placement * Refactor how `DSi`'s NAND and SD card are set - Provide them via a `DSiArgs` argument in the constructor - Give `DSi_MMCStorage` ownership of the `NANDImage` or `FATStorage` as needed, and expose getters/setters - Replace `DSi_SDHost::Ports` with a `array<unique_ptr, 2>` to reduce the risk of leaks - Store `DSi_MMCStorage`'s disk images in a `std::variant` - The SD card and NAND image are no longer reset in `Reset()`; the frontend will need to do that itself * Add getters/setters on `DSi` itself for its storage media * Remove newly-unused `Platform::ConfigEntry`s * Use `DSi::SetNAND` in the frontend * Add `EmuThread::NeedToRecreateConsole` * Document `NDSArgs` and give its fields default values * Refactor how system files are loaded upon construction - Pass `NDSArgs&&` into `NDS`'s constructor - Use `std::array` for the emulator's BIOS images and the built-in FreeBIOS, to simplify copying and comparison - Initialize the BIOS, firmware, and SD cards from `NDSArgs` or `DSiArgs` - Add a new default constructor for `NDS` (not `DSi`) that initializes the DS with default system files - Embed `FirmwareMem::Firmware` directly instead of in a `unique_ptr` - `SPIHost` now takes a `Firmware&&` that it forwards to `FirmwareMem` - Add `Firmware` getters/setters plus `const` variants for `NDS`, `Firmware`, and `FirmwareMem` - Simplify installation of firmware * Initialize the DSi BIOS in the constructor - Change `DSi::ARM9iBIOS` and `ARM7iBIOS` to `std::array` * Update the frontend to reflect the core's changes * Remove `DSi_SDHost::CloseHandles` * Pass `nullopt` instead of the empty string when folder sync is off * Deduplicate ROM extraction logic - `LoadGBAROM` and `LoadROM` now delegate to `LoadROMData` - Also use `unique_ptr` instead of `new[]` * Oops, missed some `get()`'s * Move `NDS::IsLoadedARM9BIOSBuiltIn` to the header - So it's likelier to be inlined - Same for the ARM7 version * Remove `NDS::SetConsoleType` * Add `NDS::SetFirmware` * Move `GBACart::SetupSave` to be `protected` - It was only ever used inside the class * Rename `GBACart::LoadSave` to `SetSaveMemory` - Same for the cart slot * Declare `GBACartSlot` as a friend of `GBACart::CartCommon` * Revise `GBACartSlot`'s getters and setters - Rename `InsertROM` and `LoadROM` to `SetCart` - Add a `GetCart` method * Clean up getters and setters for NDS and GBA carts * Clean up how carts are inserted into the slots - Remove setters that operate directly on pointers, to simplify error-handling (use ParseROM instead) - Add overloads for all carts that accept a `const u8*` (to copy the ROM data) and a `unique_ptr<u8[]>` (to move the ROM data) - Store all ROM and RAM data in `unique_ptr` - Default-initialize all fields - Simplify constructors and destructors, inheriting where applicable * Refactor GBA save data insertion - Make `SetupSave` private and non-virtual and move its logic to be in `SetSaveMemory` - Add overloads for setting save data in the constructor - Update the SRAM completely in `SetSaveMemory` * Clean up `NDSCart::CartCommon::SetSaveMemory` - Move its declaration next to the other `SaveMemory` methods - Move its (empty) implementation to the header * Add some comments * Add Utils.cpp and Utils.h * Rename some functions in Utils for clarity * Add `GBACart::ParseROM` and `NDSCart::ParseROM` overloads that accept `unique_ptr<u8[]>` - The `u8*` overloads delegate to these new overloads - Also move `SetupSave` for both kinds of carts to be private non-virtual methods * Finalize the `NDSCart` refactor - Add `NDSCartArgs` to pass to `ParseROM` - Add SRAM arguments for all retail carts - Initialize SRAM inside the constructor - Delegate to other constructors where possible * Replace `ROMManager::NDSSave` and `GBASave` with `unique_ptr` * Make both cart slots return the previously-inserted cart in `EjectCart` - Primarily intended for reusing carts when resetting the console * Make `NDS::EjectCart` return the old cart * Initialize both cart slots with the provided ROM (if any) * Make `NDS::EjectGBACart` return the ejected cart * Clean up some comments in Args.h * Rename `ROMManager::LoadBIOS` to `BootToMenu` - Clarifies the intent * Add `ROMManager::LoadDLDISDCard` * Add a doc comment * Refactor how the `NDS` is created or updated - Rewrite `CreateConsole` to read from `Config` and load system files, but accept carts as arguments - Fail without creating an `NDS` if any required system file doesn't load - Add `UpdateConsole`, which delegates to `CreateConsole` if switching modes or starting the app - Use `std::variant` to indicate whether a cart should be removed, inserted, or reused - Load all system files (plus SD cards) in `UpdateConsole` - Eject the cart and reinsert it into the new console if applicable * Respect some more `Config` settings in the `Load*` functions * Remove `InstallNAND` in favor of `LoadNAND` * Fix some potential bugs in `LoadROMData` * Oops, forgot to delete the definition of `InstallNAND` * Add functions to get `FATStorageArgs` - Not the cards themselves, but to get the arguments you _would_ use to load the cards * Refactor `ROMManager::LoadROM` - Load the ROM and save data before trying to initialize the console * Clean up `ROMManager::Reset` and `BootToMenu` - Let `EmuThread::UpdateConsole` do the heavy lifting * Clean up `LoadGBAROM` * Remove some unused functions * Set the default DSi BIOS to be broken in `DSiArgs` * Respect `Config::DSiFullBIOSBoot` when loading DSi BIOS files * Remove some more unused functions * Remove redundant `virtual` specifiers * Refactor `NDSCart::CartCommon::Type()` to return a member instead of a constant - One less virtual dispatch - The cart type is read in `NDSCartSlot::DoSavestate`, which is a path downstream (due to rewinding) * Remove a hash that I computed for debugging purposes * Make `ByteSwap` `constexpr` * Remove an unused `#include` * Remove unnecessary functions from the NDS carts - Mostly overrides that added nothing * Default-initialize all NDSCart fields * Make `GBACart::Type()` not rely on virtual dispatch - `GBACartSlot::DoSavestate` calls it, and savestates can be a hot path downstream * Don't forget to reset the base class in `CartGameSolarSensor::Reset()` * Remove redundant `virtual` specifiers * Default-initialize some fields in `GBACart` * Fix ROMs not loading from archives in the frontend - Whoops * Change how the `Firmware` member is declared * Forgot an include in Utils.cpp * Rename `FirmwareMem::Firmware` to `FirmwareData` to fix a build error on Linux - One of these days I'll convince you people to let me use `camelCaseMemberNames` * Add `override` to places in `DSi_MMCStorage` that warrant it * Fix firmware saving on the frontend - Remove `GetConfigString` and `ConfigEntry::WifiSettingsPath` while I'm at it * Add a non-const `GetNAND()`
2023-12-04 16:57:22 +00:00
u64 consoleid = DSi.SDMMC.GetNAND()->GetConsoleID();
// slot 0: modcrypt
*(u32*)&KeyX[0][0] = 0x746E694E;
*(u32*)&KeyX[0][4] = 0x6F646E65;
// slot 1: 'Tad'/dev.kp
*(u32*)&KeyX[1][0] = 0x4E00004A;
*(u32*)&KeyX[1][4] = 0x4A00004E;
*(u32*)&KeyX[1][8] = (u32)(consoleid >> 32) ^ 0xC80C4B72;
*(u32*)&KeyX[1][12] = (u32)consoleid;
// slot 3: console-unique eMMC crypto
*(u32*)&KeyX[3][0] = (u32)consoleid;
*(u32*)&KeyX[3][4] = (u32)consoleid ^ 0x24EE6906;
*(u32*)&KeyX[3][8] = (u32)(consoleid >> 32) ^ 0xE65B601D;
*(u32*)&KeyX[3][12] = (u32)(consoleid >> 32);
*(u32*)&KeyY[3][0] = 0x0AB9DC76;
*(u32*)&KeyY[3][4] = 0xBD4DC4D3;
*(u32*)&KeyY[3][8] = 0x202DDD1D;
}
2023-11-04 16:46:52 +00:00
void DSi_AES::DoSavestate(Savestate* file)
{
file->Section("AESi");
file->Var32(&Cnt);
file->Var32(&BlkCnt);
file->Var32(&RemExtra);
file->Var32(&RemBlocks);
file->Bool32(&OutputFlush);
file->Var32(&InputDMASize);
file->Var32(&OutputDMASize);
file->Var32(&AESMode);
InputFIFO.DoSavestate(file);
OutputFIFO.DoSavestate(file);
file->VarArray(IV, 16);
file->VarArray(MAC, 16);
file->VarArray(KeyNormal, 4*16);
file->VarArray(KeyX, 4*16);
file->VarArray(KeyY, 4*16);
file->VarArray(CurKey, 16);
file->VarArray(CurMAC, 16);
file->VarArray(OutputMAC, 16);
file->Bool32(&OutputMACDue);
file->VarArray(Ctx.RoundKey, AES_keyExpSize);
file->VarArray(Ctx.Iv, AES_BLOCKLEN);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::ProcessBlock_CCM_Extra()
{
u8 data[16];
u8 data_rev[16];
*(u32*)&data[0] = InputFIFO.Read();
*(u32*)&data[4] = InputFIFO.Read();
*(u32*)&data[8] = InputFIFO.Read();
*(u32*)&data[12] = InputFIFO.Read();
Bswap128(data_rev, data);
for (int i = 0; i < 16; i++) CurMAC[i] ^= data_rev[i];
AES_ECB_encrypt(&Ctx, CurMAC);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::ProcessBlock_CCM_Decrypt()
{
u8 data[16];
u8 data_rev[16];
2020-12-30 22:37:46 +00:00
*(u32*)&data[0] = InputFIFO.Read();
*(u32*)&data[4] = InputFIFO.Read();
*(u32*)&data[8] = InputFIFO.Read();
*(u32*)&data[12] = InputFIFO.Read();
//printf("AES-CCM: "); _printhex2(data, 16);
Bswap128(data_rev, data);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
for (int i = 0; i < 16; i++) CurMAC[i] ^= data_rev[i];
AES_ECB_encrypt(&Ctx, CurMAC);
Bswap128(data, data_rev);
//printf(" -> "); _printhex2(data, 16);
2020-12-30 22:37:46 +00:00
OutputFIFO.Write(*(u32*)&data[0]);
OutputFIFO.Write(*(u32*)&data[4]);
OutputFIFO.Write(*(u32*)&data[8]);
OutputFIFO.Write(*(u32*)&data[12]);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::ProcessBlock_CCM_Encrypt()
{
u8 data[16];
u8 data_rev[16];
2020-12-30 22:37:46 +00:00
*(u32*)&data[0] = InputFIFO.Read();
*(u32*)&data[4] = InputFIFO.Read();
*(u32*)&data[8] = InputFIFO.Read();
*(u32*)&data[12] = InputFIFO.Read();
//printf("AES-CCM: "); _printhex2(data, 16);
Bswap128(data_rev, data);
for (int i = 0; i < 16; i++) CurMAC[i] ^= data_rev[i];
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
AES_ECB_encrypt(&Ctx, CurMAC);
Bswap128(data, data_rev);
//printf(" -> "); _printhex2(data, 16);
2020-12-30 22:37:46 +00:00
OutputFIFO.Write(*(u32*)&data[0]);
OutputFIFO.Write(*(u32*)&data[4]);
OutputFIFO.Write(*(u32*)&data[8]);
OutputFIFO.Write(*(u32*)&data[12]);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::ProcessBlock_CTR()
{
u8 data[16];
u8 data_rev[16];
2020-12-30 22:37:46 +00:00
*(u32*)&data[0] = InputFIFO.Read();
*(u32*)&data[4] = InputFIFO.Read();
*(u32*)&data[8] = InputFIFO.Read();
*(u32*)&data[12] = InputFIFO.Read();
//printf("AES-CTR: "); _printhex2(data, 16);
Bswap128(data_rev, data);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
Bswap128(data, data_rev);
//printf(" -> "); _printhex(data, 16);
2020-12-30 22:37:46 +00:00
OutputFIFO.Write(*(u32*)&data[0]);
OutputFIFO.Write(*(u32*)&data[4]);
OutputFIFO.Write(*(u32*)&data[8]);
OutputFIFO.Write(*(u32*)&data[12]);
}
u32 DSi_AES::ReadCnt() const
{
u32 ret = Cnt;
2020-12-30 22:37:46 +00:00
ret |= InputFIFO.Level();
ret |= (OutputFIFO.Level() << 5);
return ret;
}
2023-11-04 16:46:52 +00:00
void DSi_AES::WriteCnt(u32 val)
{
u32 oldcnt = Cnt;
Cnt = val & 0xFC1FF000;
/*if (val & (3<<10))
{
if (val & (1<<11)) OutputFlush = true;
Update();
}*/
2019-06-20 20:42:28 +00:00
u32 dmasize_in[4] = {0, 4, 8, 12};
u32 dmasize_out[4] = {4, 8, 12, 16};
InputDMASize = dmasize_in[(val >> 12) & 0x3];
OutputDMASize = dmasize_out[(val >> 14) & 0x3];
AESMode = (val >> 28) & 0x3;
if (val & (1<<24))
{
u32 slot = (val >> 26) & 0x3;
memcpy(CurKey, KeyNormal[slot], 16);
}
if (!(oldcnt & (1<<31)) && (val & (1<<31)))
{
// transfer start (checkme)
RemExtra = (AESMode < 2) ? (BlkCnt & 0xFFFF) : 0;
RemBlocks = BlkCnt >> 16;
OutputMACDue = false;
if (AESMode == 0 && (!(val & (1<<20)))) Log(LogLevel::Debug, "AES: CCM-DECRYPT MAC FROM WRFIFO, TODO\n");
if ((RemBlocks > 0) || (RemExtra > 0))
{
u8 key[16];
u8 iv[16];
Bswap128(key, CurKey);
Bswap128(iv, IV);
if (AESMode < 2)
{
u32 maclen = (val >> 16) & 0x7;
if (maclen < 1) maclen = 1;
iv[0] = 0x02;
for (int i = 0; i < 12; i++) iv[1+i] = iv[4+i];
iv[13] = 0x00;
iv[14] = 0x00;
iv[15] = 0x01;
AES_init_ctx_iv(&Ctx, key, iv);
iv[0] |= (maclen << 3) | ((BlkCnt & 0xFFFF) ? (1<<6) : 0);
iv[13] = RemBlocks >> 12;
iv[14] = RemBlocks >> 4;
iv[15] = RemBlocks << 4;
memcpy(CurMAC, iv, 16);
AES_ECB_encrypt(&Ctx, CurMAC);
}
else
{
AES_init_ctx_iv(&Ctx, key, iv);
}
DSi.CheckNDMAs(1, 0x2A);
}
else
{
// no blocks to process? oh well. mark it finished
// CHECKME: does this trigger any IRQ or shit?
Cnt &= ~(1<<31);
}
}
//printf("AES CNT: %08X / mode=%d key=%d inDMA=%d outDMA=%d blocks=%d (BLKCNT=%08X)\n",
// val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks, BlkCnt);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::WriteBlkCnt(u32 val)
{
BlkCnt = val;
}
2023-11-04 16:46:52 +00:00
u32 DSi_AES::ReadOutputFIFO()
{
if (OutputFIFO.IsEmpty()) Log(LogLevel::Warn, "!!! AES OUTPUT FIFO EMPTY\n");
2020-12-30 22:37:46 +00:00
u32 ret = OutputFIFO.Read();
if (Cnt & (1<<31))
{
CheckInputDMA();
CheckOutputDMA();
}
else
{
2020-12-30 22:37:46 +00:00
if (OutputFIFO.Level() > 0)
DSi.CheckNDMAs(1, 0x2B);
else
DSi.StopNDMAs(1, 0x2B);
2020-12-30 22:37:46 +00:00
if (OutputMACDue && OutputFIFO.Level() <= 12)
{
2020-12-30 22:37:46 +00:00
OutputFIFO.Write(*(u32*)&OutputMAC[0]);
OutputFIFO.Write(*(u32*)&OutputMAC[4]);
OutputFIFO.Write(*(u32*)&OutputMAC[8]);
OutputFIFO.Write(*(u32*)&OutputMAC[12]);
OutputMACDue = false;
}
}
return ret;
}
2023-11-04 16:46:52 +00:00
void DSi_AES::WriteInputFIFO(u32 val)
{
// TODO: add some delay to processing
if (InputFIFO.IsFull()) Log(LogLevel::Warn, "!!! AES INPUT FIFO FULL\n");
2020-12-30 22:37:46 +00:00
InputFIFO.Write(val);
if (!(Cnt & (1<<31))) return;
Update();
}
2023-11-04 16:46:52 +00:00
void DSi_AES::CheckInputDMA()
{
if (RemBlocks == 0 && RemExtra == 0) return;
2020-12-30 22:37:46 +00:00
if (InputFIFO.Level() <= InputDMASize)
{
// trigger input DMA
DSi.CheckNDMAs(1, 0x2A);
}
Update();
}
2023-11-04 16:46:52 +00:00
void DSi_AES::CheckOutputDMA()
{
2020-12-30 22:37:46 +00:00
if (OutputFIFO.Level() >= OutputDMASize)
{
// trigger output DMA
DSi.CheckNDMAs(1, 0x2B);
}
}
2023-11-04 16:46:52 +00:00
void DSi_AES::Update()
{
if (RemExtra > 0)
{
while (InputFIFO.Level() >= 4 && RemExtra > 0)
{
ProcessBlock_CCM_Extra();
RemExtra--;
}
}
if (RemExtra == 0)
{
while (InputFIFO.Level() >= 4 && OutputFIFO.Level() <= 12 && RemBlocks > 0)
{
switch (AESMode)
{
case 0: ProcessBlock_CCM_Decrypt(); break;
case 1: ProcessBlock_CCM_Encrypt(); break;
case 2:
case 3: ProcessBlock_CTR(); break;
}
RemBlocks--;
}
}
CheckOutputDMA();
2019-06-19 20:08:35 +00:00
if (RemBlocks == 0 && RemExtra == 0)
2019-06-19 20:08:35 +00:00
{
if (AESMode == 0)
{
Ctx.Iv[13] = 0x00;
Ctx.Iv[14] = 0x00;
Ctx.Iv[15] = 0x00;
AES_CTR_xcrypt_buffer(&Ctx, CurMAC, 16);
//printf("FINAL MAC: "); _printhexR(CurMAC, 16);
//printf("INPUT MAC: "); _printhex(MAC, 16);
Cnt |= (1<<21);
for (int i = 0; i < 16; i++)
{
if (CurMAC[15-i] != MAC[i]) Cnt &= ~(1<<21);
}
}
else if (AESMode == 1)
{
Ctx.Iv[13] = 0x00;
Ctx.Iv[14] = 0x00;
Ctx.Iv[15] = 0x00;
AES_CTR_xcrypt_buffer(&Ctx, CurMAC, 16);
Bswap128(OutputMAC, CurMAC);
if (OutputFIFO.Level() <= 12)
{
OutputFIFO.Write(*(u32*)&OutputMAC[0]);
OutputFIFO.Write(*(u32*)&OutputMAC[4]);
OutputFIFO.Write(*(u32*)&OutputMAC[8]);
OutputFIFO.Write(*(u32*)&OutputMAC[12]);
}
else
OutputMACDue = true;
// CHECKME
Cnt &= ~(1<<21);
}
else
{
// CHECKME
Cnt &= ~(1<<21);
}
2019-06-19 20:08:35 +00:00
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) DSi.SetIRQ2(IRQ2_DSi_AES);
DSi.StopNDMAs(1, 0x2A);
2020-12-30 22:37:46 +00:00
if (!OutputFIFO.IsEmpty())
DSi.CheckNDMAs(1, 0x2B);
else
DSi.StopNDMAs(1, 0x2B);
OutputFlush = false;
2019-06-19 20:08:35 +00:00
}
}
2023-11-04 16:46:52 +00:00
void DSi_AES::WriteIV(u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&IV[offset];
*(u32*)&IV[offset] = (old & ~mask) | (val & mask);
//printf("AES: IV: "); _printhex(IV, 16);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::WriteMAC(u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&MAC[offset];
*(u32*)&MAC[offset] = (old & ~mask) | (val & mask);
//printf("AES: MAC: "); _printhex(MAC, 16);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::ROL16(u8* val, u32 n)
{
u32 n_coarse = n >> 3;
u32 n_fine = n & 7;
u8 tmp[16];
for (u32 i = 0; i < 16; i++)
{
tmp[i] = val[(i - n_coarse) & 0xF];
}
for (u32 i = 0; i < 16; i++)
{
val[i] = (tmp[i] << n_fine) | (tmp[(i - 1) & 0xF] >> (8-n_fine));
}
}
void DSi_AES::DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey)
{
const u8 key_const[16] = {0xFF, 0xFE, 0xFB, 0x4E, 0x29, 0x59, 0x02, 0x58, 0x2A, 0x68, 0x0F, 0x5F, 0x1A, 0x4F, 0x3E, 0x79};
u8 tmp[16];
for (int i = 0; i < 16; i++)
tmp[i] = keyX[i] ^ keyY[i];
u32 carry = 0;
for (int i = 0; i < 16; i++)
{
u32 res = tmp[i] + key_const[15-i] + carry;
tmp[i] = res & 0xFF;
carry = res >> 8;
}
ROL16(tmp, 42);
memcpy(normalkey, tmp, 16);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyNormal[slot][offset];
*(u32*)&KeyNormal[slot][offset] = (old & ~mask) | (val & mask);
//printf("KeyNormal(%d): ", slot); _printhex(KeyNormal[slot], 16);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyX[slot][offset];
*(u32*)&KeyX[slot][offset] = (old & ~mask) | (val & mask);
//printf("KeyX(%d): ", slot); _printhex(KeyX[slot], 16);
}
2023-11-04 16:46:52 +00:00
void DSi_AES::WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyY[slot][offset];
*(u32*)&KeyY[slot][offset] = (old & ~mask) | (val & mask);
//printf("[%08X] KeyY(%d): ", NDS::GetPC(1), slot); _printhex(KeyY[slot], 16);
if (offset >= 0xC)
{
DeriveNormalKey(KeyX[slot], KeyY[slot], KeyNormal[slot]);
}
}
}