int ctr_nand_crypto_interface_initialize(ctr_nand_crypto_interface *crypto_io, uint8_t keySlot, ctr_nand_crypto_type crypto_type, ctr_io_interface *lower_io) { crypto_io->base = nand_crypto_base; //Get the nonces for CTRNAND and TWL decryption uint32_t mode; uint32_t NandCid[4]; alignas(4) uint8_t shasum[32]; sdmmc_get_cid(true, NandCid); uint32_t ctr[4]; switch (crypto_type) { case NAND_CTR: check_and_do_n3ds_init(); sha_init(SHA256_MODE); sha_update((uint8_t*)NandCid, 16); sha_get(shasum); memcpy(ctr, shasum, 16); mode = AES_CNT_CTRNAND_MODE; break; case NAND_TWL: check_and_do_twl_init(); sha_init(SHA1_MODE); sha_update((uint8_t*)NandCid, 16); sha_get(shasum); for(uint32_t i = 0; i < 16u; i++) // little endian and reversed order { ((uint8_t*)ctr)[i] = shasum[15-i]; } mode = AES_CNT_TWLNAND_MODE; break; default: return 1; //Unknown type } ctr_crypto_interface_initialize(&crypto_io->crypto_io, keySlot, mode, CTR_CRYPTO_ENCRYPTED, CRYPTO_CTR, (uint8_t*)ctr, lower_io); return 0; }
// Protocol // 4.2.3 Card Initialization and Identification Process static sdmmc_rt sdmmc_card_init(void) { sdmmc_rt r; int i; sd_sm->rca = 0; sd_sm->is_mmc = 0; sd_sm->is_bus4bit = 0; sd_sm->ci_stat = SDP_IDLE; for (i = 0; i < 1000; i++); sdmmc_cmd_noarg(CMD0_GO_IDLE_STATE); printk("SDPROT\tIdle State(idle)\n"); r = sdmmc_cmd(CMD8R7_SEND_IF_COND, CMD8_VHS_27to36 | CMD8_CHECK_PATTERN); printk("SDMMC cmd8() %s\n", sdmmc_err_string(r)); if (r == SDMMC_NO_RSP) { printk("SDPROT\tVer2.00 or later SD Memory Card(voltage mismatch)\n"); printk("\tor Ver1.X SD Memory Card\n"); printk("\tor not SD Memory Card\n"); } else if ((sdmmc_resp() & CMD8_CHECK_PATTERN_MASK) == CMD8_CHECK_PATTERN) { printk("SDPROT\tVer2.00 or later SD Memory Card\n"); } else { // unsupported return SDMMC_UNSUP; } if ((r = ACMD41()) == SDMMC_OK) { printk("SDPROT\tCard returns ready\n"); printk("\tVer1.X Standard Capacity SD Memory Card\n"); } else { printk("SDPROT\tNo Response(Non valid command)\n"); printk("\tMust be a MultiMediaCard\n"); printk("SDPORT\tStart MultiMediaCard initialization process\n"); printk("\tstarting at CMD1\n"); if ((r = CMD1()) != SDMMC_OK) { return r; } sd_sm->is_mmc = 1; sd_sm->rca = MMC_RCA; } printk("SDPROT\tReady State(ready)\n"); for (i = 0; i < SDMMC_CMD_RETRY; i++) { r = sdmmc_cmd_noarg(CMD2R2_ALL_SEND_CID); if (r == SDMMC_OK) break; } if (r != SDMMC_OK) { return r; } printk("SDPROT\tIdentification State(ident)\n"); sdmmc_get_cid(); // sdprot_print_cid(&sd_sm->cid); for (i = 0; i < SDMMC_CMD_RETRY; i++) { uint32_t arg = 0; if (sd_sm->is_mmc) { arg = sd_sm->rca << 16; } r = sdmmc_cmd(CMD3R6_SEND_RELATIVE, arg); if (r == SDMMC_OK) break; } if (r != SDMMC_OK) { return r; } printk("SDPROT\tCard responds with new RCA\n"); if (!sd_sm->is_mmc) { sd_sm->rca = (sdmmc_resp() >> 16); }
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); }
u32 SelfTest(u32 param) { u8* test_data = (u8*) 0x20316000; const u8 teststr[16] = { 'D', '9', ' ', 'S', 'E', 'L', 'F', 'T', 'E', 'S', 'T', ' ', ' ', ' ', ' ' }; const u8 zeroes[16] = { 0x00 }; bool selftest = !(param & ST_REFERENCE); // check keyslots Debug("Checking keyslots..."); Debug("0x05 KeyY: %s", (CheckKeySlot(0x05, 'Y') == 0) ? "set up" : "not set up"); Debug("0x25 KeyX: %s", (CheckKeySlot(0x25, 'X') == 0) ? "set up" : "not set up"); Debug("0x18 KeyX: %s", (CheckKeySlot(0x18, 'X') == 0) ? "set up" : "not set up"); Debug("0x1B KeyX: %s", (CheckKeySlot(0x1B, 'X') == 0) ? "set up" : "not set up"); Debug(""); Debug((selftest) ? "Running selftest..." : "Creating selftest reference data..."); // process all subtests u32 num_tests = sizeof(TestList) / sizeof(SubTestInfo); u8* test_ptr = test_data; u32 fsize_test = 0; for (u32 i = 0; i < num_tests; i++) { u32 size = TestList[i].size; u32 size_a = align(size, 16); u32 type = TestList[i].type; u32 tparam = TestList[i].param; memset(test_ptr, 0x00, 16 + size_a); strncpy((char*) test_ptr, TestList[i].name, 16); test_ptr += 16; if (type == ST_NAND_CID_HARD) { sdmmc_get_cid(1, (uint32_t*) test_ptr); } else if (type == ST_NAND_CID_MEM) { memcpy(test_ptr, (void*) 0x01FFCD84, 16); } else if (type == ST_SHA) { sha_quick(test_ptr, teststr, 16, tparam); } else if ((type == ST_AES_MODE) || (type == ST_AES_KEYSLOT) || (type == ST_AES_KEYSLOT_Y)) { CryptBufferInfo info = {.setKeyY = 0, .size = 16, .buffer = test_ptr}; if (type == ST_AES_MODE) { info.mode = tparam; info.keyslot = 0x11; setup_aeskey(0x11, (void*) zeroes); } else { if (type == ST_AES_KEYSLOT_Y) { info.setKeyY = 1; memcpy(info.keyY, zeroes, 16); } info.mode = AES_CNT_CTRNAND_MODE; info.keyslot = tparam; } memset(info.ctr, 0x00, 16); memcpy(test_ptr, teststr, 16); CryptBuffer(&info); } else if (type == ST_TITLEKEYS) { TitleKeyEntry titlekey; memset(&titlekey, 0x00, sizeof(TitleKeyEntry)); for (titlekey.commonKeyIndex = 0; titlekey.commonKeyIndex < 6; titlekey.commonKeyIndex++) { memset(titlekey.titleId, 0x00, 8); memset(titlekey.titleKey, 0x00, 16); CryptTitlekey(&titlekey, false); memcpy(test_ptr + (titlekey.commonKeyIndex * 16), titlekey.titleKey, 16); } } test_ptr += size_a; fsize_test += 16 + size_a; }