int SetupTMDInfoRecord(tmd_content_info_record *info_record, u8 *content_record, u16 ContentCount) { memset(info_record,0x0,sizeof(tmd_content_info_record)*0x40); u16_to_u8(info_record->contentIndexOffset,0x0,BE); u16_to_u8(info_record->contentCommandCount,ContentCount,BE); ctr_sha(content_record,sizeof(tmd_content_chunk)*ContentCount,info_record->contentChunkHash,CTR_SHA_256); return 0; }
int GenerateTicket(USER_CONTEXT *ctx) { if(ctx->flags[verbose]) { printf("[+] Generating Ticket\n"); } ctx->cia_section[tik].size = (sizeof(TICKET_STRUCTURE)+ sizeof(TIK_2048_SIG_CONTEXT)); ctx->cia_section[tik].buffer = malloc(ctx->cia_section[tik].size); if(ctx->cia_section[tik].buffer == NULL){ printf("[!] Failed to allocated memory for ticket\n"); return 1; } TICKET_STRUCTURE ticket; TIK_2048_SIG_CONTEXT sig; memset(&sig,0x0,sizeof(TIK_2048_SIG_CONTEXT)); memset(&ticket,0x0,sizeof(TICKET_STRUCTURE)); if(ctx->flags[verbose]) { printf(" > Collecting Data\n"); } ticket.TicketFormatVersion = ctx->core.ticket_format_ver; ticket.ca_crl_version = ctx->core.ca_crl_version; ticket.signer_crl_version = ctx->core.signer_crl_version; ticket.CommonKeyID = ctx->keys.common_key_id; memcpy(ticket.Issuer,ctx->core.TicketIssuer,0x40); memcpy(ticket.TicketID,ctx->core.TicketID,0x8); memcpy(ticket.DeviceID,ctx->core.DeviceID,0x4); memcpy(ticket.TitleID,ctx->core.TitleID,0x8); memcpy(ticket.TicketVersion,ctx->core.TicketVersion,0x2); if(ctx->flags[verbose]) { printf(" > Encrypting Titlekey\n"); } if(EncryptTitleKey(ticket.EncryptedTitleKey,ctx->keys.title_key,ctx->keys.common_key,ctx->core.TitleID) != 0){ printf("[!] Failed to encrypt titlekey\n"); return Fail; } if(ctx->flags[showkeys]) memdump(stdout,"\n[+] Encrypted Title Key: ",ticket.EncryptedTitleKey,0x10); if(SetStaticData(dev,ticket.StaticData) != 0){ printf("[!] ERROR in Generating Ticket\n"); return Fail; } if(ctx->flags[verbose]) { printf(" > Signing Ticket\n"); } u32_to_u8(sig.sig_type,RSA_2048_SHA256,BE); u8 hash[0x20]; ctr_sha(&ticket,sizeof(TICKET_STRUCTURE),hash,CTR_SHA_256); if(ctr_rsa(hash,sig.data,ctx->keys.ticket.n,ctx->keys.ticket.d,RSA_2048_SHA256,CTR_RSA_SIGN) != Good){ printf("[!] Failed to sign ticket\n"); return ticket_gen_fail; } if(ctx->flags[info]){ memdump(stdout,"[+] Ticket Signature: ",sig.data,0x100); } memcpy(ctx->cia_section[tik].buffer,&sig,sizeof(TIK_2048_SIG_CONTEXT)); memcpy((ctx->cia_section[tik].buffer + sizeof(TIK_2048_SIG_CONTEXT)),&ticket,sizeof(TICKET_STRUCTURE)); return 0; }
int VerifyNCCHSection(USER_CONTEXT *ctx, u8 cxi_key[0x10], u32 offset, FILE *ncch) { NCCH_STRUCT *cxi_ctx = malloc(sizeof(NCCH_STRUCT)); if(cxi_ctx == NULL){ printf("[!] Memory Allocation Failure\n"); return Fail; } memset(cxi_ctx,0x0,sizeof(NCCH_STRUCT)); GetCXIStruct(cxi_ctx,offset,ncch); u8 HeaderSignature[0x100]; u8 Header[0x100]; u8 HeaderSHAHash[0x20]; u8 ExHeader[0x800]; memset(&HeaderSignature,0x0,0x100); memset(&Header,0x0,0x100); memset(&HeaderSHAHash,0x0,0x20); fseek(ncch,offset+0x0,SEEK_SET); fread(HeaderSignature,0x100,1,ncch); fread(Header,0x100,1,ncch); ctr_sha(&Header,0x100,HeaderSHAHash,CTR_SHA_256); RSA_2048_KEY HeaderRSA; memset(&HeaderRSA,0x0,sizeof(RSA_2048_KEY)); u8 Exponent[0x3] = {0x01,0x00,0x01}; memcpy(HeaderRSA.e,Exponent,0x3); if(cxi_ctx->is_cfa == True) memcpy(HeaderRSA.n,ctx->keys.NcsdCfa.n,0x100); else{ memset(&ExHeader,0x0,0x800); fseek(ncch,offset+cxi_ctx->exheader_offset,SEEK_SET); fread(&ExHeader,0x800,1,ncch); if(cxi_ctx->encrypted == True){ u8 counter[0x10]; ncch_get_counter(cxi_ctx,counter,NCCHTYPE_EXHEADER); ctr_aes_context aes_ctx; memset(&aes_ctx,0x0,sizeof(ctr_aes_context)); ctr_init_counter(&aes_ctx, cxi_key, counter); ctr_crypt_counter(&aes_ctx, ExHeader, ExHeader, 0x800); if(memcmp((ExHeader+0x200),cxi_ctx->programID,8) != 0){ printf("[!] CXI decryption failed\n"); return Fail; } } memcpy(HeaderRSA.n,ExHeader+0x500,0x100); } return ctr_rsa(HeaderSHAHash,HeaderSignature,HeaderRSA.n,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); }
int GenerateExeFS_Header(exefs_buildctx *ctx, u8 *outbuff) { for(int i = 0; i < ctx->fileCount; i++){ if(i == 0) ctx->fileOffset[i] = 0; else ctx->fileOffset[i] = align((ctx->fileOffset[i-1]+ctx->fileSize[i-1]),ctx->blockSize); memcpy(ctx->fileHdr[i].name,ctx->fileName[i],8); u32_to_u8(ctx->fileHdr[i].offset,ctx->fileOffset[i],LE); u32_to_u8(ctx->fileHdr[i].size,ctx->fileSize[i],LE); ctr_sha(ctx->file[i],ctx->fileSize[i],ctx->fileHashes[9-i],CTR_SHA_256); } memcpy(outbuff,ctx->fileHdr,sizeof(exefs_filehdr)*10); memcpy(outbuff+0xc0,ctx->fileHashes,0x20*10); return 0; }
int SetupTMDHeader(tmd_hdr *hdr, tmd_content_info_record *info_record, cia_settings *ciaset) { memset(hdr,0,sizeof(tmd_hdr)); memcpy(hdr->issuer,ciaset->tmd.issuer,0x40); hdr->formatVersion = ciaset->tmd.formatVersion; hdr->caCrlVersion = ciaset->cert.caCrlVersion; hdr->signerCrlVersion = ciaset->cert.signerCrlVersion; memcpy(hdr->titleID,ciaset->common.titleId,8); memcpy(hdr->titleType,ciaset->tmd.titleType,4); memcpy(hdr->savedataSize,ciaset->tmd.savedataSize,4); memcpy(hdr->privSavedataSize,ciaset->tmd.privSavedataSize,4); hdr->twlFlag = ciaset->tmd.twlFlag; u16_to_u8(hdr->titleVersion,ciaset->tmd.version,BE); u16_to_u8(hdr->contentCount,ciaset->content.count,BE); ctr_sha(info_record,sizeof(tmd_content_info_record)*64,hdr->infoRecordHash,CTR_SHA_256); return 0; }
int VerifyNCSD(USER_CONTEXT *ctx, FILE *ncsd) { u8 HeaderSignature[0x100]; u8 Header[0x100]; u8 HeaderSHAHash[0x20]; memset(&HeaderSignature,0x0,0x100); memset(&Header,0x0,0x100); memset(&HeaderSHAHash,0x0,0x20); fseek(ncsd,0x0,SEEK_SET); fread(HeaderSignature,0x100,1,ncsd); fread(Header,0x100,1,ncsd); ctr_sha(&Header,0x100,HeaderSHAHash,CTR_SHA_256); switch(ctr_rsa(HeaderSHAHash,HeaderSignature,ctx->keys.NcsdCfa.n,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY)){ case Good : printf("[+] NCSD header is valid\n"); break; case Fail : printf("[+] NCSD header is invalid\n"); break; } return 0; }
int PrepAESMAC(AESMAC_CTX *ctx) { u8 savegame_type[6][8] = {"CTR-EXT0","CTR-SYS0","CTR-NOR0","CTR-SAV0","CTR-SIGN","CTR-9DB0"}; memset(ctx->hash,0,0x20); if(ctx->type == 0) return Fail; else if(ctx->type == mac_import_db){ db_sha256_block finalblock; memset(&finalblock,0,sizeof(db_sha256_block)); memcpy(finalblock.savegame_type,savegame_type[CTR_9DB0],8); u32_to_u8(finalblock.db_id,0x3,LE); memcpy(finalblock.diff_header,ctx->header,0x100); ctr_sha(&finalblock,sizeof(db_sha256_block),ctx->hash,CTR_SHA_256); } else if(ctx->type == mac_title_db){ db_sha256_block finalblock; memset(&finalblock,0,sizeof(db_sha256_block)); memcpy(finalblock.savegame_type,savegame_type[CTR_9DB0],8); u32_to_u8(finalblock.db_id,0x2,LE); memcpy(finalblock.diff_header,ctx->header,0x100); ctr_sha(&finalblock,sizeof(db_sha256_block),ctx->hash,CTR_SHA_256); } else if(ctx->type == mac_extdata){ extdata_sha256_block finalblock; memset(&finalblock,0,sizeof(extdata_sha256_block)); memcpy(finalblock.savegame_type,savegame_type[CTR_EXT0],8); endian_memcpy(finalblock.image_id,ctx->image_id,4,LE); endian_memcpy(finalblock.subdir_id,ctx->subdir_id,4,LE); u32_to_u8(finalblock.unknown,ctx->is_quote_dat,LE); endian_memcpy(finalblock.image_id_dup,ctx->image_id,4,LE); endian_memcpy(finalblock.subdir_id_dup,ctx->subdir_id,4,LE); memcpy(finalblock.diff_header,ctx->header,0x100); ctr_sha(&finalblock,sizeof(extdata_sha256_block),ctx->hash,CTR_SHA_256); } else if(ctx->type == mac_sys_save){ sys_savedata_sha256_block finalblock; memset(&finalblock,0,sizeof(sys_savedata_sha256_block)); memcpy(finalblock.savegame_type,savegame_type[CTR_SYS0],8); endian_memcpy(finalblock.save_id,ctx->saveid,8,LE); memcpy(finalblock.disa_header,ctx->header,0x100); ctr_sha(&finalblock,sizeof(sys_savedata_sha256_block),ctx->hash,CTR_SHA_256); } else if(ctx->type == mac_sd_save){ sdcard_savegame_sha256_block finalblock; memset(&finalblock,0,sizeof(sdcard_savegame_sha256_block)); memcpy(finalblock.savegame_type,savegame_type[CTR_SIGN],8); endian_memcpy(finalblock.program_id,ctx->programid,8,LE); // Generating CTR_SAV0 hash ctr_sav0_sha256_block pre_block; memset(&pre_block,0,sizeof(ctr_sav0_sha256_block)); memcpy(pre_block.savegame_type,savegame_type[CTR_SAV0],8); memcpy(pre_block.disa_header,ctx->header,0x100); ctr_sha(&pre_block,sizeof(ctr_sav0_sha256_block),finalblock.ctr_sav0_block_hash,CTR_SHA_256); // ctr_sha(&finalblock,sizeof(sdcard_savegame_sha256_block),ctx->hash,CTR_SHA_256); } else if(ctx->type == mac_card_save){ gamecard_savegame_sha256_block finalblock; memset(&finalblock,0,sizeof(gamecard_savegame_sha256_block)); memcpy(finalblock.savegame_type,savegame_type[CTR_SAV0],8); // Generating CTR_NOR0 hash ctr_nor0_sha256_block pre_block; memset(&pre_block,0,sizeof(ctr_nor0_sha256_block)); memcpy(pre_block.savegame_type,savegame_type[CTR_NOR0],8); memcpy(pre_block.disa_header,ctx->header,0x100); ctr_sha(&pre_block,sizeof(ctr_nor0_sha256_block),finalblock.ctr_nor0_block_hash,CTR_SHA_256); // ctr_sha(&finalblock,sizeof(gamecard_savegame_sha256_block),ctx->hash,CTR_SHA_256); } else return Fail; return 0; }
int GetNCSDData(USER_CONTEXT *ctx, NCSD_STRUCT *ncsd_struct, FILE *ncsd) { if(ncsd_struct == NULL) return Fail; memset(ncsd_struct,0x0,sizeof(NCSD_STRUCT)); NCSD_HEADER header; CARD_INFO_HEADER card_info; DEV_CARD_INFO_HEADER dev_card_info; fseek(ncsd,0x0,SEEK_SET); fread(&ncsd_struct->signature,0x100,1,ncsd); fseek(ncsd,0x100,SEEK_SET); fread(&header,sizeof(NCSD_HEADER),1,ncsd); fseek(ncsd,0x200,SEEK_SET); fread(&card_info,sizeof(CARD_INFO_HEADER),1,ncsd); fseek(ncsd,0x1200,SEEK_SET); fread(&dev_card_info,sizeof(DEV_CARD_INFO_HEADER),1,ncsd); ctr_sha(&header,sizeof(NCSD_HEADER),ncsd_struct->ncsd_header_hash,CTR_SHA_256); if(u8_to_u32(header.magic,BE) != NCSD_MAGIC){ printf("[!] ROM is Corrupt\n"); return Fail; } ncsd_struct->sig_valid = ctr_rsa(ncsd_struct->ncsd_header_hash,ncsd_struct->signature,ctx->keys.NcsdCfa.n,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); u32 media_size = ((header.partition_flags[6] + 1)*0x200); ncsd_struct->rom_size = u8_to_u32(header.rom_size,LE)*media_size; ncsd_struct->used_rom_size = 0; if(ncsd_struct->used_rom_size == 0){ u32 tmp = u8_to_u32(header.offsetsize_table[0].offset,LE); for(int i = 0; i < 8; i++){ tmp += u8_to_u32(header.offsetsize_table[i].size,LE); } ncsd_struct->used_rom_size = tmp*media_size; } for(int i = 0; i < 8; i++){ ncsd_struct->partition_data[i].offset = u8_to_u32(header.offsetsize_table[i].offset,LE)*media_size; ncsd_struct->partition_data[i].size = u8_to_u32(header.offsetsize_table[i].size,LE)*media_size; if(ncsd_struct->partition_data[i].offset != 0 && ncsd_struct->partition_data[i].size != 0) ncsd_struct->partition_data[i].active = True; ncsd_struct->partition_data[i].title_id = u8_to_u64(header.partition_id_table[i],LE); ncsd_struct->partition_data[i].fs_type = header.partitions_fs_type[i]; ncsd_struct->partition_data[i].crypto_type = header.partitions_crypto_type[i]; u8 magic[4]; fseek_64(ncsd,(ncsd_struct->partition_data[i].offset + 0x100),SEEK_SET); fread(&magic,4,1,ncsd); if(u8_to_u32(magic,BE) == NCCH_MAGIC){ u8 flags[8]; u8 flag_bool[8]; fseek_64(ncsd,(ncsd_struct->partition_data[i].offset + 0x188),SEEK_SET); fread(&flags,8,1,ncsd); resolve_flag(flags[5],flag_bool); if(flag_bool[1] == False && flag_bool[0] == True){ if(flag_bool[2] == False && flag_bool[3] == True) ncsd_struct->partition_data[i].content_type = CFA_Manual; else if(flag_bool[2] == True && flag_bool[3] == True) ncsd_struct->partition_data[i].content_type = CFA_DLPChild; else if(flag_bool[2] == True && flag_bool[3] == False) ncsd_struct->partition_data[i].content_type = CFA_Update; else ncsd_struct->partition_data[i].content_type = _unknown; } else if(flag_bool[1] == True) ncsd_struct->partition_data[i].content_type = CXI; else ncsd_struct->partition_data[i].content_type = _unknown; } else ncsd_struct->partition_data[i].content_type = _unknown; } if(u8_to_u64(card_info.cver_title_id,LE) == 0){ u8 stock_title_key[0x10] = {0x6E, 0xC7, 0x5F, 0xB2, 0xE2, 0xB4, 0x87, 0x46, 0x1E, 0xDD, 0xCB, 0xB8, 0x97, 0x11, 0x92, 0xBA}; if(memcmp(dev_card_info.TitleKey,stock_title_key,0x10) == 0) ncsd_struct->type = dev_external_SDK; else ncsd_struct->type = dev_internal_SDK; } else ncsd_struct->type = retail; /** if(ncsd_struct->type != retail){ u8 iv[16]; u8 key[16]; memset(&iv,0x0,16); memset(&key,0x0,16); //iv[0] = header.partition_flags[7]; //memcpy(iv+11,header.partition_flags,5); u8 tmp[16] = {0xB2, 0x57, 0xA7, 0xC0, 0x24, 0xC8, 0xC1, 0xB0, 0x75, 0x91, 0xC4, 0xC5, 0x1D, 0x96, 0x67, 0x4F}; memcpy(iv,tmp,16); memcpy(key,common_dpki_aesKey,16); ctr_aes_context aes; memset(&aes,0x0,sizeof(ctr_aes_context)); memdump(stdout,"ENC Title Key: ",dev_card_info.TitleKey,0x10); ctr_init_aes_cbc(&aes,key,iv,DEC); ctr_aes_cbc(&aes,dev_card_info.TitleKey,dev_card_info.TitleKey,0x10,DEC); memdump(stdout,"DEC Title Key: ",dev_card_info.TitleKey,0x10); } **/ /** for(int i = 0; i < 8; i++){ if(ncsd_struct->partition_data[i].active == True){ ncsd_struct->partition_data[i].sig_valid = VerifyNCCHSection(ctx,dev_card_info.TitleKey,ncsd_struct->partition_data[i].offset,ncsd); } } **/ ncsd_struct->valid = True; if(ctx->flags[info]) PrintNCSDData(ncsd_struct,&header,&card_info,&dev_card_info); return 0; }
int FinaliseNcch(ncch_settings *set) { u8 *ncch = set->out->buffer; ncch_hdr *hdr = (ncch_hdr*)ncch; u8 *exhdr = (u8*)(ncch + set->cryptoDetails.exhdrOffset); u8 *acexDesc = (u8*)(ncch + set->cryptoDetails.acexOffset); u8 *logo = (u8*)(ncch + set->cryptoDetails.logoOffset); u8 *exefs = (u8*)(ncch + set->cryptoDetails.exefsOffset); u8 *romfs = (u8*)(ncch + set->cryptoDetails.romfsOffset); // Taking Hashes if(set->cryptoDetails.exhdrSize) ctr_sha(exhdr,set->cryptoDetails.exhdrSize,hdr->exhdrHash,CTR_SHA_256); if(set->cryptoDetails.logoSize) ctr_sha(logo,set->cryptoDetails.logoSize,hdr->logoHash,CTR_SHA_256); if(set->cryptoDetails.exefsHashDataSize) ctr_sha(exefs,set->cryptoDetails.exefsHashDataSize,hdr->exefsHash,CTR_SHA_256); if(set->cryptoDetails.romfsHashDataSize) ctr_sha(romfs,set->cryptoDetails.romfsHashDataSize,hdr->romfsHash,CTR_SHA_256); // Signing NCCH int sig_result = Good; if(set->options.IsCfa) sig_result = SignCFA(hdr,set->keys); else sig_result = SignCXI(hdr,set->keys); if(sig_result != Good){ fprintf(stderr,"[NCCH ERROR] Failed to sign %s header\n",set->options.IsCfa ? "CFA" : "CXI"); return sig_result; } // Crypting NCCH\n"); if(IsNcchEncrypted(hdr)){ if(!SetNcchKeys(set->keys, hdr)){ fprintf(stderr,"[NCCH ERROR] Failed to load NCCH AES key\n"); return -1; } if(set->options.verbose){ printf("[NCCH] NCCH AES keys:\n"); memdump(stdout," > key0: ",set->keys->aes.ncchKey0,AES_128_KEY_SIZE); memdump(stdout," > key1: ",set->keys->aes.ncchKey1,AES_128_KEY_SIZE); } // Crypting Exheader/AcexDesc if(set->cryptoDetails.exhdrSize){ CryptNcchRegion(exhdr,set->cryptoDetails.exhdrSize,0x0,&set->cryptoDetails,set->keys->aes.ncchKey0,ncch_exhdr); CryptNcchRegion(acexDesc,set->cryptoDetails.acexSize,set->cryptoDetails.exhdrSize,&set->cryptoDetails,set->keys->aes.ncchKey0,ncch_exhdr); } // Crypting ExeFs Files if(set->cryptoDetails.exefsSize){ exefs_hdr *exefsHdr = (exefs_hdr*)exefs; for(int i = 0; i < MAX_EXEFS_SECTIONS; i++){ u8 *key = NULL; if(strncmp(exefsHdr->fileHdr[i].name,"icon",8) == 0 || strncmp(exefsHdr->fileHdr[i].name,"banner",8) == 0) key = set->keys->aes.ncchKey0; else key = set->keys->aes.ncchKey1; u32 offset = u8_to_u32(exefsHdr->fileHdr[i].offset,LE) + sizeof(exefs_hdr); u32 size = u8_to_u32(exefsHdr->fileHdr[i].size,LE); if(size) CryptNcchRegion((exefs+offset),align(size,set->options.blockSize),offset,&set->cryptoDetails,key,ncch_exefs); } // Crypting ExeFs Header CryptNcchRegion(exefs,sizeof(exefs_hdr),0x0,&set->cryptoDetails,set->keys->aes.ncchKey0,ncch_exefs); } // Crypting RomFs if(set->cryptoDetails.romfsSize) CryptNcchRegion(romfs,set->cryptoDetails.romfsSize,0x0,&set->cryptoDetails,set->keys->aes.ncchKey1,ncch_romfs); } return 0; }