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 DecryptTitlekeys(void) { EncKeysInfo *info = (EncKeysInfo*)0x20316000; if (!DebugFileOpen("/encTitleKeys.bin")) return 1; if (!DebugFileRead(info, 16, 0)) { FileClose(); return 1; } if (!info->n_entries || info->n_entries > MAX_ENTRIES) { Debug("Too many/few entries specified: %i", info->n_entries); FileClose(); return 1; } Debug("Number of entries: %i", info->n_entries); if (!DebugFileRead(info->entries, info->n_entries * sizeof(TitleKeyEntry), 16)) { FileClose(); return 1; } FileClose(); Debug("Decrypting Title Keys..."); u8 ctr[16] __attribute__((aligned(32))); u8 keyY[16] __attribute__((aligned(32))); u32 i; for (i = 0; i < info->n_entries; i++) { memset(ctr, 0, 16); memcpy(ctr, info->entries[i].titleId, 8); set_ctr(AES_BIG_INPUT|AES_NORMAL_INPUT, ctr); memcpy(keyY, (void *)common_keyy[info->entries[i].commonKeyIndex], 16); setup_aeskey(0x3D, AES_BIG_INPUT|AES_NORMAL_INPUT, keyY); use_aeskey(0x3D); aes_decrypt(info->entries[i].encryptedTitleKey, info->entries[i].encryptedTitleKey, ctr, 1, AES_CBC_DECRYPT_MODE); } if (!DebugFileCreate("/decTitleKeys.bin", true)) return 1; if (!DebugFileWrite(info, info->n_entries * sizeof(TitleKeyEntry) + 16, 0)) { FileClose(); return 1; } FileClose(); Debug("Done!"); return 0; }
ret_type CCM_decrypt(const unsigned char imsg[], /* the plaintext input message */ unsigned char omsg[], /* the encrypted output message */ const mlen_type len, /* the length of this block (bytes) */ CCM_ctx ctx[1]) /* the CCM context */ { mlen_type cnt = (mlen_type)-1, t_cnt = ctx->cnt, hi = ctx->md_len - t_cnt; if(len > hi) { if(len != hi + ctx->af_len) return CCM_msg_length_error; } else hi = len; while(++cnt < hi) { omsg[cnt] = imsg[cnt] ^ ctx->sii[t_cnt & BLOCK_MASK]; /* decrypt message */ ctx->cbc[t_cnt & BLOCK_MASK] ^= omsg[cnt]; /* update the CBC */ if(!(++t_cnt & BLOCK_MASK)) /* if the current encryption block is full */ { inc_ctr(ctx); aes_enc_blk(ctx->blk, ctx->sii, ctx->aes); /* encrypt the CTR value */ aes_enc_blk(ctx->cbc, ctx->cbc, ctx->aes); /* encrypt the running CBC */ } } if(t_cnt == ctx->md_len) /* if at end of message */ { if(t_cnt & BLOCK_MASK) /* if a partial block remains */ aes_enc_blk(ctx->cbc, ctx->cbc, ctx->aes); set_ctr(ctx, 0); /* set CTR to zero */ aes_enc_blk(ctx->blk, ctx->sii, ctx->aes); /* encrypt the CTR */ /* store the encrypted authentication value */ for(t_cnt = 0; t_cnt < ctx->af_len; ++t_cnt) if(imsg[cnt + t_cnt] != (ctx->cbc[t_cnt] ^ ctx->sii[t_cnt])) { /* if bad clear the message and authentication field */ memset(omsg, 0, (size_t)cnt + ctx->af_len); return CCM_auth_failure; } } else ctx->cnt = t_cnt; return (ret_type)cnt; }
ret_type CCM_encrypt(const unsigned char imsg[], /* the plaintext input message */ unsigned char omsg[], /* the encrypted output message */ const mlen_type len, /* the length of this block (bytes) */ CCM_ctx ctx[1]) /* the CCM context */ { mlen_type cnt = (mlen_type)-1, t_cnt = ctx->cnt; if(len > ctx->md_len - t_cnt) return CCM_msg_length_error; while(++cnt < len) { ctx->cbc[t_cnt & BLOCK_MASK] ^= imsg[cnt]; /* update the CBC */ omsg[cnt] = imsg[cnt] ^ ctx->sii[t_cnt & BLOCK_MASK]; /* encrypt message */ if(!(++t_cnt & BLOCK_MASK)) /* if the current encryption block is full */ { inc_ctr(ctx); aes_enc_blk(ctx->blk, ctx->sii, ctx->aes); /* encrypt the CTR value */ aes_enc_blk(ctx->cbc, ctx->cbc, ctx->aes); /* encrypt the running CBC */ } } if(t_cnt == ctx->md_len) /* if at end of message */ { if(t_cnt & BLOCK_MASK) /* if a partial block remains */ aes_enc_blk(ctx->cbc, ctx->cbc, ctx->aes); set_ctr(ctx, 0); /* set CTR to zero */ aes_enc_blk(ctx->blk, ctx->sii, ctx->aes); /* encrypt the CTR */ /* encrypt and store the authentication value */ for(t_cnt = 0; t_cnt < ctx->af_len; ++t_cnt) omsg[cnt + t_cnt] = ctx->cbc[t_cnt] ^ ctx->sii[t_cnt]; cnt += ctx->af_len; } else ctx->cnt = t_cnt; return (ret_type)cnt; }
u32 DecryptPartition(PartitionInfo* info){ size_t bytesWritten; if (info->keyY != NULL) setup_aeskey(info->keyslot, AES_BIG_INPUT | AES_NORMAL_INPUT, info->keyY); use_aeskey(info->keyslot); u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->ctr, 16); u32 size_bytes = info->size; for (u32 i = 0; i < size_bytes; i += BLOCK_SIZE) { u32 j; for (j = 0; (j < BLOCK_SIZE) && (i + j < size_bytes); j += 16) { set_ctr(AES_BIG_INPUT | AES_NORMAL_INPUT, ctr); aes_decrypt((void*)info->buffer + j, (void*)info->buffer + j, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 1); } } return 0; }
u32 DecryptPartition(PartitionInfo* info){ size_t bytesWritten; if(info->keyY != NULL) setup_aeskey(info->keyslot, AES_BIG_INPUT|AES_NORMAL_INPUT, info->keyY); use_aeskey(info->keyslot); u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->ctr, 16); u32 size_bytes = info->size; for (u32 i = 0; i < size_bytes; i += BLOCK_SIZE) { u32 j; for (j = 0; (j < BLOCK_SIZE) && (i+j < size_bytes); j+= 16) { set_ctr(AES_BIG_INPUT|AES_NORMAL_INPUT, ctr); aes_decrypt((void*)info->buffer+j, (void*)info->buffer+j, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 1); TryScreenShot(); //Putting it here allows us to take screenshots at any decryption point, since everyting loops in this } } return 0; }
u32 DecryptBuffer(DecryptBufferInfo *info) { u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->CTR, 16); u8* buffer = info->buffer; u32 size = info->size; if (info->setKeyY) { setup_aeskey(info->keyslot, AES_BIG_INPUT | AES_NORMAL_INPUT, info->keyY); info->setKeyY = 0; } use_aeskey(info->keyslot); for (u32 i = 0; i < size; i += 0x10, buffer += 0x10) { set_ctr(AES_BIG_INPUT | AES_NORMAL_INPUT, ctr); aes_decrypt((void*) buffer, (void*) buffer, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 0x1); } memcpy(info->CTR, ctr, 16); return 0; }
u32 NandPadgen() { u8* ctrStart = FindNandCtr(); if (ctrStart == NULL) return 1; u8 ctr[16] = {0x0}; u32 i = 0; for(i = 0; i < 16; i++) ctr[i] = *(ctrStart + (15 - i)); //The CTR is stored backwards in memory. add_ctr(ctr, 0xB93000); //The CTR stored in memory would theoretically be for NAND block 0, so we need to increment it some. u32 keyslot = 0x0; u32 nand_size = 0; switch (GetUnitPlatform()) { case PLATFORM_3DS: keyslot = 0x4; nand_size = 758; break; case PLATFORM_N3DS: keyslot = 0x5; nand_size = 1055; break; } Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size); Debug("Filename: nand.fat16.xorpad"); PadInfo padInfo = {.keyslot = keyslot, .setKeyY = 0, .size_mb = nand_size , .filename = "/nand.fat16.xorpad"}; memcpy(padInfo.CTR, ctr, 16); u32 result = CreatePad(&padInfo); if(result == 0) { Debug("Done!"); return 0; } else { return 1; } } u32 CreatePad(PadInfo *info) { static const uint8_t zero_buf[16] __attribute__((aligned(16))) = {0}; u8* buffer = BUFFER_ADDRESS; u32 result = 0; if (!FileCreate(info->filename, true)) // No DebugFileCreate() here - messages are already given return 1; if(info->setKeyY) setup_aeskey(info->keyslot, AES_BIG_INPUT | AES_NORMAL_INPUT, info->keyY); use_aeskey(info->keyslot); u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->CTR, 16); u32 size_bytes = info->size_mb * 1024*1024; for (u32 i = 0; i < size_bytes; i += BUFFER_MAX_SIZE) { u32 curr_block_size = min(BUFFER_MAX_SIZE, size_bytes - i); for (u32 j = 0; j < curr_block_size; j+= 16) { set_ctr(AES_BIG_INPUT | AES_NORMAL_INPUT, ctr); aes_decrypt((void*)zero_buf, (void*)buffer + j, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 1); } ShowProgress(i, size_bytes); if (!DebugFileWrite((void*)buffer, curr_block_size, i)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; } u32 NandDumper() { u8* buffer = BUFFER_ADDRESS; u32 nand_size = (GetUnitPlatform() == PLATFORM_3DS) ? 0x3AF00000 : 0x4D800000; u32 result = 0; Debug("Dumping System NAND. Size (MB): %u", nand_size / (1024 * 1024)); if (!DebugFileCreate("/NAND.bin", true)) return 1; u32 n_sectors = nand_size / NAND_SECTOR_SIZE; for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { ShowProgress(i, n_sectors); sdmmc_nand_readsectors(i, SECTORS_PER_READ, buffer); if(!DebugFileWrite(buffer, NAND_SECTOR_SIZE * SECTORS_PER_READ, i * NAND_SECTOR_SIZE)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; } u32 NandPartitionsDumper() { u32 ctrnand_offset; u32 ctrnand_size; u32 keyslot; switch (GetUnitPlatform()) { case PLATFORM_3DS: ctrnand_offset = 0x0B95CA00; ctrnand_size = 0x2F3E3600; keyslot = 0x4; break; case PLATFORM_N3DS: ctrnand_offset = 0x0B95AE00; ctrnand_size = 0x41D2D200; keyslot = 0x5; break; } // see: http://3dbrew.org/wiki/Flash_Filesystem Debug("Dumping firm0.bin: %s!", DumpPartition("firm0.bin", 0x0B130000, 0x00400000, 0x6) == 0 ? "succeeded" : "failed"); Debug("Dumping firm1.bin: %s!", DumpPartition("firm1.bin", 0x0B530000, 0x00400000, 0x6) == 0 ? "succeeded" : "failed"); Debug("Dumping ctrnand.bin: %s!", DumpPartition("ctrnand.bin", ctrnand_offset, ctrnand_size, keyslot) == 0 ? "succeeded" : "failed"); return 0; }
ret_type CCM_init( const unsigned char key[], const unsigned long key_len, /* the key value to be used */ const unsigned char nonce[], /* the nonce value */ const unsigned char auth[], const unsigned long ad_len, /* the additional authenticated data */ const mlen_type msg_len, /* message data length */ const unsigned long auth_field_len, /* the authentication field length */ CCM_ctx ctx[1]) /* the CCM context */ { aes_32t cnt; if(aes_enc_key(key, key_len, ctx->aes) == aes_bad) return CCM_bad_key; /* bad key value */ if(auth_field_len < 2 || auth_field_len > 16 || (auth_field_len & 1)) return CCM_bad_auth_field_length; /* illegal authentication field size */ if(ad_len >= 65536ul - 256ul) return CCM_bad_auth_data_length; /* too much added authetication data */ /* save length values and compile the blocks for the running CBC and CTR values */ ctx->md_len = msg_len; ctx->af_len = auth_field_len; #ifndef LONG_MESSAGES ctx->blk[0] = (ctx->md_len & 0xff000000 ? 3 : ctx->md_len & 0xffff0000 ? 2 : 1); #else ctx->blk[0] = (ctx->md_len & 0xff00000000000000 ? 7 : ctx->md_len & 0xffff000000000000 ? 6 : ctx->md_len & 0xffffff0000000000 ? 5 : ctx->md_len & 0xffffffff00000000 ? 4 : ctx->md_len & 0xffffffffff000000 ? 3 : ctx->md_len & 0xffffffffffff0000 ? 2 : 1); #endif /* move the nonce into the block */ for(cnt = 0; cnt < (aes_32t)BLOCK_SIZE - ctx->blk[0] - 2; ++cnt) ctx->blk[cnt + 1] = nonce[cnt]; set_ctr(ctx, ctx->md_len); /* set message length value */ memcpy(ctx->cbc, ctx->blk, BLOCK_SIZE); /* and copy into running CBC */ ctx->cbc[0] |= (ad_len ? 0x40 : 0) + ((auth_field_len - 2) << 2); set_ctr(ctx, 1); /* initial counter value = 1 */ aes_enc_blk(ctx->cbc, ctx->cbc, ctx->aes); /* encrypt the cbc block */ aes_enc_blk(ctx->blk, ctx->sii, ctx->aes); /* encrypt counter block */ if(ad_len) /* if there is additional authentication data */ { cnt = 0; /* set the two byte length field for the data */ ctx->cbc[0] ^= (aes_08t)(ad_len >> 8); ctx->cbc[1] ^= (aes_08t) ad_len; while(cnt < ad_len) /* perform the CBC calculation on the data */ { /* xor data into the running CBC block */ ctx->cbc[(cnt + 2) & BLOCK_MASK] ^= auth[cnt]; /* if CBC block is full or at end of the authentication data */ if(!((++cnt + 2) & BLOCK_MASK) || cnt == ad_len) aes_enc_blk(ctx->cbc, ctx->cbc, ctx->aes); } } ctx->cnt = 0; return CCM_ok; }
uint32_t NcchPadgen() { uint32_t result; File pf; NcchInfo *info = (NcchInfo*)0x20316000; const char *filename = "/ncchinfo.bin"; wchar_t wfilename[14]; mbstowcs(wfilename, filename, 14); if (!FileOpen(&pf, filename, 0)) { print(strings[STR_ERROR_OPENING], filename+1); return 1; } FileRead(&pf, info, 16, 0); if (info->ncch_info_version != 0xF0000003) { print(strings[STR_WRONG], filename+1, strings[STR_VERSION]); return 0; } if (!info->n_entries || info->n_entries > MAXENTRIES) { print(strings[STR_WRONG], filename+1, strings[STR_ENTRIES_COUNT]); return 0; } FileRead(&pf, info->entries, info->n_entries * sizeof(NcchInfoEntry), 16); FileClose(&pf); print(strings[STR_PROCESSING], wfilename+1); ConsoleShow(); for(uint32_t i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.setKeyY = 1, .size_mb = info->entries[i].size_mb}; memcpy(padInfo.CTR, info->entries[i].CTR, 16); memcpy(padInfo.keyY, info->entries[i].keyY, 16); memcpy(padInfo.filename, info->entries[i].filename, 112); if(info->entries[i].uses7xCrypto) padInfo.keyslot = 0x25; else padInfo.keyslot = 0x2C; result = CreatePad(&padInfo, i); if (result) return 1; } return 0; } uint32_t SdPadgen() { size_t bytesRead; uint32_t result; File fp; SdInfo *info = (SdInfo*)0x20316000; uint8_t movable_seed[0x120] = {0}; const char *filename = "/movable.sed"; wchar_t wfilename[13]; mbstowcs(wfilename, filename, 13); // Load console 0x34 keyY from movable.sed if present on SD card if (FileOpen(&fp, filename, 0)) { bytesRead = FileRead(&fp, &movable_seed, 0x120, 0); FileClose(&fp); if (bytesRead != 0x120) { print(strings[STR_WRONG], filename+1, strings[STR_SIZE]); return 1; } if (memcmp(movable_seed, "SEED", 4) != 0) { print(strings[STR_WRONG], filename+1, strings[STR_CONTENT]); return 1; } setup_aeskey(0x34, AES_BIG_INPUT|AES_NORMAL_INPUT, &movable_seed[0x110]); use_aeskey(0x34); } filename = "/SDinfo.bin"; if (!FileOpen(&fp, filename, 0)) { print(strings[STR_ERROR_OPENING], filename+1); return 1; } bytesRead = FileRead(&fp, info, 4, 0); if (!info->n_entries || info->n_entries > MAXENTRIES) { print(strings[STR_WRONG], filename+1, strings[STR_ENTRIES_COUNT]); return 1; } print(strings[STR_PROCESSING], wfilename+1); ConsoleShow(); bytesRead = FileRead(&fp, info->entries, info->n_entries * sizeof(SdInfoEntry), 4); FileClose(&fp); for(uint32_t i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb}; memcpy(padInfo.CTR, info->entries[i].CTR, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); result = CreatePad(&padInfo, i); if (result) return 1; } return 0; } static const uint8_t zero_buf[16] __attribute__((aligned(16))) = {0}; uint32_t CreatePad(PadInfo *info, int index) { File pf; #define BUFFER_ADDR ((volatile uint8_t*)0x21000000) #define BLOCK_SIZE (4*1024*1024) if (!FileOpen(&pf, info->filename, 1)) return 1; if(info->setKeyY != 0) setup_aeskey(info->keyslot, AES_BIG_INPUT|AES_NORMAL_INPUT, info->keyY); use_aeskey(info->keyslot); uint8_t ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->CTR, 16); uint32_t size_bytes = info->size_mb*1024*1024; uint32_t size_100 = size_bytes/100; uint32_t seekpos = 0; for (uint32_t i = 0; i < size_bytes; i += BLOCK_SIZE) { uint32_t j; for (j = 0; (j < BLOCK_SIZE) && (i+j < size_bytes); j+= 16) { set_ctr(AES_BIG_INPUT|AES_NORMAL_INPUT, ctr); aes_decrypt((void*)zero_buf, (void*)BUFFER_ADDR+j, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 1); } print(strings[STR_GENERATING], strings[STR_PAD]); print(L"%i : %i%%", index, (i+j)/size_100); ConsolePrevLine(); ConsolePrevLine(); ConsoleShow(); FileWrite(&pf, (void*)BUFFER_ADDR, j, seekpos); seekpos += j; } FileClose(&pf); return 0; }