Files
pico-loader/arm7/source/loader/TwlAes.cpp

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();
(&REG_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*)&REG_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*)&REG_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);
}