void ctr_twl_keyslot_setup(void) { //Only a9lh really needs to bother with this, and it really only needs to happen once, before ITCM gets messed up. static bool setup = false; if (!setup && ctr_detect_a9lh_entry()) { uint32_t* TwlCustId = (uint32_t*) (0x01FFB808); uint32_t TwlKeyX[4]; alignas(32) uint8_t TwlKeyY[16]; // thanks b1l1s & Normmatt // see source from https://gbatemp.net/threads/release-twltool-dsi-downgrading-save-injection-etc-multitool.393488/ const char* nintendo = "NINTENDO"; TwlKeyX[0] = (TwlCustId[0] ^ 0xB358A6AF) | 0x80000000; TwlKeyX[3] = TwlCustId[1] ^ 0x08C267B7; memcpy(TwlKeyX + 1, nintendo, 8); // see: https://www.3dbrew.org/wiki/Memory_layout#ARM9_ITCM uint32_t TwlKeyYW3 = 0xE1A00005; memcpy(TwlKeyY, (uint8_t*) 0x01FFD3C8, 12); memcpy(TwlKeyY + 12, &TwlKeyYW3, 4); setup_aeskeyX(0x03, (uint8_t*)TwlKeyX); setup_aeskeyY(0x03, TwlKeyY); use_aeskey(0x03); } setup = true; }
u32 CryptBuffer(CryptBufferInfo *info) { u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->ctr, 16); u8* buffer = info->buffer; u32 size = info->size; u32 mode = info->mode; if (info->setKeyY) { u8 keyY[16] __attribute__((aligned(32))); memcpy(keyY, info->keyY, 16); setup_aeskeyY(info->keyslot, keyY); info->setKeyY = 0; } use_aeskey(info->keyslot); for (u32 i = 0; i < size; i += 0x10, buffer += 0x10) { if (((mode & (0x7 << 27)) != AES_ECB_DECRYPT_MODE) && ((mode & (0x7 << 27)) != AES_ECB_ENCRYPT_MODE)) set_ctr(ctr); if ((mode & (0x7 << 27)) == AES_CBC_DECRYPT_MODE) memcpy(ctr, buffer, 0x10); aes_decrypt((void*) buffer, (void*) buffer, 1, mode); if ((mode & (0x7 << 27)) == AES_CBC_ENCRYPT_MODE) memcpy(ctr, buffer, 0x10); else if ((mode & (0x7 << 27)) == AES_CTR_MODE) add_ctr(ctr, 0x1); } memcpy(info->ctr, ctr, 16); return 0; }
u32 SdPadgen(u32 param) { (void) (param); // param is unused here SdInfo *info = (SdInfo*) 0x20316000; // setup AES key from SD SetupSdKeyY0x34(false, NULL); if (!DebugFileOpen("SDinfo.bin")) return 1; if (!DebugFileRead(info, 4, 0)) { FileClose(); return 1; } if (!info->n_entries || info->n_entries > MAX_ENTRIES) { FileClose(); Debug("Bad number of entries!"); return 1; } if (!DebugFileRead(info->entries, info->n_entries * sizeof(SdInfoEntry), 4)) { FileClose(); return 1; } FileClose(); Debug("Number of entries: %i", info->n_entries); for(u32 i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb, .mode = AES_CNT_CTRNAND_MODE}; memcpy(padInfo.ctr, info->entries[i].ctr, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); Debug ("%2i: %s (%iMB)", i, info->entries[i].filename, info->entries[i].size_mb); if (CreatePad(&padInfo) != 0) return 1; // this can't fail anyways } return 0; } u32 SdPadgenDirect(u32 param) { (void) (param); // param is unused here SdInfo *info = (SdInfo*) 0x20316000; char basepath[256]; u8 movable_keyY[16]; if (SetupSdKeyY0x34(true, movable_keyY) != 0) return 1; // movable.sed has to be present in NAND Debug(""); if (SdFolderSelector(basepath, movable_keyY) != 0) return 1; Debug(""); if (SdInfoGen(info, basepath) != 0) return 1; if (!info->n_entries) { Debug("Nothing found in folder"); return 1; } Debug("Number of entries: %i", info->n_entries); for(u32 i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb, .mode = AES_CNT_CTRNAND_MODE}; memcpy(padInfo.ctr, info->entries[i].ctr, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); Debug ("%2i: %s (%iMB)", i, info->entries[i].filename, info->entries[i].size_mb); if (CreatePad(&padInfo) != 0) return 1; // this can't fail anyways } return 0; } u32 AnyPadgen(u32 param) { (void) (param); // param is unused here AnyPadInfo *info = (AnyPadInfo*) 0x20316000; // get header if ((FileGetData("anypad.bin", info, 16, 0) != 16) || !info->n_entries || info->n_entries > MAX_ENTRIES) { Debug("Corrupt or not existing: anypad.bin"); return 1; } // get data u32 data_size = info->n_entries * sizeof(AnyPadInfoEntry); if (FileGetData("anypad.bin", (u8*) info + 16, data_size, 16) != data_size) { Debug("File is missing data: anypad.bin"); return 1; } Debug("Processing anypad.bin..."); Debug("Number of entries: %i", info->n_entries); for (u32 i = 0; i < info->n_entries; i++) { // this translates all entries to a standard padInfo struct AnyPadInfoEntry* entry = &(info->entries[i]); PadInfo padInfo = {.keyslot = entry->keyslot, .setKeyY = 0, .size_mb = 0, .size_b = entry->size_b, .mode = entry->mode}; memcpy(padInfo.filename, entry->filename, 80); memcpy(padInfo.ctr, entry->ctr, 16); // process keys if (entry->setNormalKey) setup_aeskey(entry->keyslot, entry->normalKey); if (entry->setKeyX) setup_aeskeyX(entry->keyslot, entry->keyX); if (entry->setKeyY) setup_aeskeyY(entry->keyslot, entry->keyY); use_aeskey(entry->keyslot); // process flags if (entry->flags & (AP_USE_NAND_CTR|AP_USE_SD_CTR)) { u32 ctr_add = getbe32(padInfo.ctr + 12); u8 shasum[32]; u8 cid[16]; sdmmc_get_cid((entry->flags & AP_USE_NAND_CTR) ? 1 : 0, (uint32_t*) cid); if (entry->mode == AES_CNT_TWLNAND_MODE) { sha_quick(shasum, cid, 16, SHA1_MODE); for (u32 i = 0; i < 16; i++) padInfo.ctr[i] = shasum[15-i]; } else { sha_quick(shasum, cid, 16, SHA256_MODE); memcpy(padInfo.ctr, shasum, 16); } add_ctr(padInfo.ctr, ctr_add); } // create the pad Debug ("%2i: %s (%ikB)", i, entry->filename, entry->size_b / 1024); if (CreatePad(&padInfo) != 0) return 1; // this can't fail anyways } return 0; } u32 CtrNandPadgen(u32 param) { char* filename = (param & PG_FORCESLOT4) ? "nand.fat16.slot0x04.xorpad" : "nand.fat16.xorpad"; u32 keyslot; u32 nand_size; // legacy sizes & offset, to work with Python 3DSFAT16Tool if (GetUnitPlatform() == PLATFORM_3DS) { if (param & PG_FORCESLOT4) { Debug("This is a N3DS only feature"); return 1; } keyslot = 0x4; nand_size = 758; } else { keyslot = (param & PG_FORCESLOT4) ? 0x4 : 0x5; nand_size = 1055; } Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size); Debug("Filename: %s", filename); PadInfo padInfo = { .keyslot = keyslot, .setKeyY = 0, .size_mb = nand_size, .mode = AES_CNT_CTRNAND_MODE }; strncpy(padInfo.filename, filename, 64); if(GetNandCtr(padInfo.ctr, 0xB930000) != 0) return 1; return CreatePad(&padInfo); } u32 TwlNandPadgen(u32 param) { (void) (param); // param is unused here PartitionInfo* twln_info = GetPartitionInfo(P_TWLN); u32 size_mb = (twln_info->size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating TWLN FAT16 xorpad. Size (MB): %u", size_mb); Debug("Filename: twlnand.fat16.xorpad"); PadInfo padInfo = { .keyslot = twln_info->keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "twlnand.fat16.xorpad", .mode = AES_CNT_TWLNAND_MODE }; if(GetNandCtr(padInfo.ctr, twln_info->offset) != 0) return 1; return CreatePad(&padInfo); } u32 Firm0Firm1Padgen(u32 param) { (void) (param); // param is unused here PartitionInfo* firm0_info = GetPartitionInfo(P_FIRM0); PartitionInfo* firm1_info = GetPartitionInfo(P_FIRM1); u32 size_mb = (firm0_info->size + firm1_info->size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating FIRM0FIRM1 xorpad. Size (MB): %u", size_mb); Debug("Filename: firm0firm1.xorpad"); PadInfo padInfo = { .keyslot = firm0_info->keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "firm0firm1.xorpad", .mode = AES_CNT_CTRNAND_MODE }; if(GetNandCtr(padInfo.ctr, firm0_info->offset) != 0) return 1; return CreatePad(&padInfo); }