mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-06-02 09:16:49 +02:00
Initial commit
This commit is contained in:
153
arm7/source/loader/TwlAes.cpp
Normal file
153
arm7/source/loader/TwlAes.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#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->ntrHeader.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->ntrHeader.gameCode,
|
||||
__builtin_bswap32(romHeader->ntrHeader.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);
|
||||
}
|
||||
Reference in New Issue
Block a user