public static void DecryptNCA(Stream input, long offset) { var keyset = new Keyset(); using (var bw = new BinaryWriterX(input, true)) using (var br = new BinaryReaderX(input, true)) { br.BaseStream.Position = offset; //Decrypt NCA Header var enc_header = br.ReadBytes(0x400); var magic = enc_header.GetElements(0x200, 4).Aggregate("", (o, b) => o + (char)b); var headerPart = new byte[0x400]; var headerPart2 = new byte[0x800]; if (magic != "NCA2" && magic != "NCA3") { headerPart = Decryption.XTS128(enc_header, keyset.ncaHeaderKey, 0x200, true); magic = headerPart.GetElements(0x200, 4).Aggregate("", (o, b) => o + (char)b); bw.BaseStream.Position = offset; bw.Write(headerPart); if (magic == "NCA3") { headerPart2 = Decryption.XTS128(br.ReadBytes(0x800), keyset.ncaHeaderKey, 0x200, true, 2); bw.BaseStream.Position = offset + 0x400; bw.Write(headerPart2); } else if (magic == "NCA2") { for (int i = 0; i < 4; i++) { var buffer = Decryption.XTS128(br.ReadBytes(0x200), keyset.ncaHeaderKey, 0x200, true); Array.Copy(buffer, 0, headerPart2, i * 0x200, 0x200); bw.BaseStream.Position -= 0x200; bw.Write(buffer); } } else { throw new InvalidDataException("Invalid NCA Header! Are the keys correct?"); } } else { headerPart = enc_header; headerPart2 = br.ReadBytes(0x800); } //Get crypto_type for master_key var cryptoType = (headerPart[0x220] > headerPart[0x206]) ? headerPart[0x220] : headerPart[0x206]; if (cryptoType == 1) { cryptoType--; } //RightsID bool hasRightsID = false; for (int i = 0; i < 0x10; i++) { if (headerPart[0x230 + i] != 0) { hasRightsID = true; break; } } /* Explanation: * - if a RightsID is set, an external titleKey is needed. This titleKey is contained in a ticket, * most likely installed on the system by download titles from eshop * - if no RightsID is set, the nca internal keyarea is used to decrypt the contents */ byte[] dec_title_key = null; byte[] dec_key_area = null; if (!hasRightsID) { //Decrypt keyarea var keyIndex = headerPart[0x207]; if (keyIndex > 2) { throw new InvalidDataException($"NCA KeyIndex must be 0-2. Found KeyIndex: {keyIndex}"); } dec_key_area = Decryption.ECB128(headerPart.GetElements(0x300, 0x40), keyset.keyAreaKeys[cryptoType][keyIndex]); } else { //Decrypt title_key var title_key = InputBox.Show("Input Titlekey:", "Decrypt NCA").Hexlify(16); dec_title_key = Decryption.ECB128(title_key, keyset.titleKeks[cryptoType]); } //Read out section crypto List <SectionEntry> sectionList = new BinaryReaderX(new MemoryStream(headerPart.GetElements(0x240, 0x40))).ReadMultiple <SectionEntry>(4); for (int i = 0; i < 4; i++) { if (sectionList[i].mediaOffset != 0 && sectionList[i].endMediaOffset != 0) { br.BaseStream.Position = offset + sectionList[i].mediaOffset * 0x200; if (hasRightsID) { var enc_buffer = br.ReadBytes(sectionList[i].endMediaOffset * 0x200 - sectionList[i].mediaOffset * 0x200); var dec_buffer = Decryption.CTR128(enc_buffer, dec_title_key, GenerateCTR(i + 1, sectionList[i].mediaOffset * 0x200)); bw.BaseStream.Position = offset + sectionList[i].mediaOffset * 0x200; bw.Write(dec_buffer); } else { var sectionCrypto = headerPart2[i * 0x200 + 0x4]; if (sectionCrypto == 0 || sectionCrypto > 4) { throw new InvalidDataException($"SectionCrypto {i} must be 1-4. Found SectionCrypto: {sectionCrypto}"); } switch (sectionCrypto) { //case 1 is NoCrypto case 2: //XTS var enc_buffer = br.ReadBytes(sectionList[i].endMediaOffset * 0x200 - sectionList[i].mediaOffset * 0x200); var dec_buffer = Decryption.XTS128(enc_buffer, dec_key_area.GetElements(0, 0x20), 0x200); bw.BaseStream.Position = offset + sectionList[i].mediaOffset * 0x200; bw.Write(dec_buffer); break; case 3: //CTR enc_buffer = br.ReadBytes(sectionList[i].endMediaOffset * 0x200 - sectionList[i].mediaOffset * 0x200); dec_buffer = Decryption.CTR128(enc_buffer, dec_key_area.GetElements(0x20, 0x10), GenerateCTR(i + 1, sectionList[i].mediaOffset * 0x200)); bw.BaseStream.Position = offset + sectionList[i].mediaOffset * 0x200; bw.Write(dec_buffer); break; case 4: //BKTR //stub break; } bw.BaseStream.Position = offset + 0x400 + i * 0x200 + 0x4; bw.Write((byte)0x1); } } } } }