mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-01-10 08:29:29 +01:00
154 lines
4.2 KiB
C++
154 lines
4.2 KiB
C++
#include "common.h"
|
|
#include <libtwl/sys/twlScfg.h>
|
|
#include <libtwl/sys/twlFuse.h>
|
|
#include <libtwl/dma/dmaTwl.h>
|
|
#include "TwlAes.h"
|
|
|
|
#define KEY_SLOT_MODULE 0
|
|
#define KEY_SLOT_NAND 3
|
|
|
|
#define MODULE_KEYX_NINT 0x746E694E // Nint
|
|
#define MODULE_KEYX_ENDO 0x6F646E65 // endo
|
|
|
|
#define NAND_KEYX_DSI_XOR_LO 0x24EE6906
|
|
#define NAND_KEYX_DSI_XOR_HI 0xE65B601D
|
|
|
|
#define NAND_KEYX_3DS_NINT 0x544E494E // NINT
|
|
#define NAND_KEYX_3DS_ENDO 0x4F444E45 // ENDO
|
|
|
|
#define NAND_KEYY_WORD_3 0xE1A00005 // mov r0, r5
|
|
|
|
#define DMA_CHANNEL_AES_OUT 0
|
|
#define DMA_CHANNEL_AES_IN 1
|
|
|
|
void TwlAes::SetupAes(const nds_header_twl_t* romHeader) const
|
|
{
|
|
// ensure AES is enabled
|
|
REG_SCFG_EXT |= SCFG_EXT_AES;
|
|
REG_SCFG_CLK |= SCFG_CLK_AES;
|
|
|
|
REG_AES_CNT = 0;
|
|
|
|
aes_reset();
|
|
aes_reset();
|
|
aes_waitKeyBusy();
|
|
|
|
SetupModuleKeyXY(romHeader);
|
|
SetupNandKeyX();
|
|
aes_waitKeyBusy();
|
|
(®_AES_SEED0)[KEY_SLOT_NAND * 3].words[3] = NAND_KEYY_WORD_3;
|
|
}
|
|
|
|
void TwlAes::DecryptModuleAes(void* data, u32 length, const aes_u128_t* iv) const
|
|
{
|
|
REG_AES_CNT = 0;
|
|
|
|
aes_reset();
|
|
aes_reset();
|
|
aes_waitKeyBusy();
|
|
|
|
aes_setKeySlot(0);
|
|
aes_waitKeyBusy();
|
|
|
|
u32 offset = 0;
|
|
while (length > 0)
|
|
{
|
|
REG_AES_CNT = 0;
|
|
aes_reset();
|
|
u32 blockLength = std::min<u32>(length, 0xFFFF0u);
|
|
|
|
auto ctr = *iv;
|
|
((u64*)&ctr)[1] += __builtin_add_overflow(((u64*)&ctr)[0], offset >> 4, &((u64*)&ctr)[0]);
|
|
aes_setInitializationVector(&ctr);
|
|
aes_setPayloadBlockCount(blockLength >> 4);
|
|
|
|
LOG_DEBUG("%p\n", (u8*)data + offset);
|
|
|
|
dma_twl_config_t inputDmaConfig
|
|
{
|
|
.src = (u8*)data + offset,
|
|
.dst = (void*)®_AES_IFIFO,
|
|
.totalWordCount = blockLength >> 2,
|
|
.wordCount = 4,
|
|
.blockInterval = NDMABCNT_INTERVAL(8),
|
|
.fillData = 0,
|
|
.control = NDMACNT_DST_MODE_FIXED | NDMACNT_SRC_MODE_INCREMENT |
|
|
NDMACNT_PHYSICAL_COUNT_4 | NDMACNT_MODE_AES_IN | NDMACNT_ENABLE
|
|
};
|
|
dma_twlSetParams(DMA_CHANNEL_AES_IN, &inputDmaConfig);
|
|
|
|
dma_twl_config_t outputDmaConfig
|
|
{
|
|
.src = (const void*)®_AES_OFIFO,
|
|
.dst = (u8*)data + offset,
|
|
.totalWordCount = blockLength >> 2,
|
|
.wordCount = 4,
|
|
.blockInterval = NDMABCNT_INTERVAL(8),
|
|
.fillData = 0,
|
|
.control = NDMACNT_SRC_MODE_FIXED | NDMACNT_DST_MODE_INCREMENT |
|
|
NDMACNT_PHYSICAL_COUNT_4 | NDMACNT_MODE_AES_OUT | NDMACNT_ENABLE
|
|
};
|
|
dma_twlSetParams(DMA_CHANNEL_AES_OUT, &outputDmaConfig);
|
|
|
|
aes_start(
|
|
AES_CNT_INPUT_FIFO_DMA_SIZE_4_BYTES |
|
|
AES_CNT_OUTPUT_FIFO_DMA_SIZE_4_BYTES |
|
|
AES_CNT_MODE_CTR);
|
|
dma_twlWait(DMA_CHANNEL_AES_OUT);
|
|
aes_waitBusy();
|
|
|
|
offset += blockLength;
|
|
length -= blockLength;
|
|
}
|
|
}
|
|
|
|
void TwlAes::SetupModuleKeyXY(const nds_header_twl_t* romHeader) const
|
|
{
|
|
if ((romHeader->twlFlags & (1 << 2)) || (romHeader->twlFlags2 & (1 << 7)))
|
|
{
|
|
// debug
|
|
aes_setKey(KEY_SLOT_MODULE, (const aes_u128_t*)romHeader);
|
|
}
|
|
else
|
|
{
|
|
// retail
|
|
aes_u128_t keyX { .words =
|
|
{
|
|
MODULE_KEYX_NINT,
|
|
MODULE_KEYX_ENDO,
|
|
romHeader->gameCode,
|
|
__builtin_bswap32(romHeader->gameCode)
|
|
}};
|
|
aes_setKeyXY(KEY_SLOT_MODULE, &keyX, (const aes_u128_t*)romHeader->arm9iSha1Hmac);
|
|
}
|
|
}
|
|
|
|
void TwlAes::SetupNandKeyX() const
|
|
{
|
|
if ((REG_SCFG_A7ROM & SCFG_A7ROM_DISABLE_FUSE) || (REG_FUSE_VERIFY & FUSE_VERIFY_ERROR))
|
|
{
|
|
// No access to the console id register. We'll assume nand key x is already setup.
|
|
return;
|
|
}
|
|
|
|
u32 consoleIdLo = REG_FUSE_ID0;
|
|
u32 consoleIdHi = REG_FUSE_ID1;
|
|
|
|
aes_u128_t keyX;
|
|
keyX.words[0] = consoleIdLo;
|
|
if (consoleIdLo & 0x80000000)
|
|
{
|
|
// 3DS
|
|
keyX.words[1] = NAND_KEYX_3DS_NINT;
|
|
keyX.words[2] = NAND_KEYX_3DS_ENDO;
|
|
}
|
|
else
|
|
{
|
|
// DSi
|
|
keyX.words[1] = consoleIdLo ^ NAND_KEYX_DSI_XOR_LO;
|
|
keyX.words[2] = consoleIdHi ^ NAND_KEYX_DSI_XOR_HI;
|
|
}
|
|
keyX.words[3] = consoleIdHi;
|
|
aes_setKeyX(KEY_SLOT_NAND, &keyX);
|
|
}
|