#include "common.h" #include #include #include #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(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); }