From 6e13e9e4c468df2764eecd0643f7e4f86f66658e Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 13 Oct 2024 11:56:03 -0400 Subject: [PATCH] Make CIATool consistent with ThreeDSTool --- NDecrypt.N3DS/CIATool.cs | 308 ++++++++++++++++----------------------- 1 file changed, 125 insertions(+), 183 deletions(-) diff --git a/NDecrypt.N3DS/CIATool.cs b/NDecrypt.N3DS/CIATool.cs index c822d31..0355f7c 100644 --- a/NDecrypt.N3DS/CIATool.cs +++ b/NDecrypt.N3DS/CIATool.cs @@ -25,29 +25,9 @@ namespace NDecrypt.N3DS private readonly bool _development; /// - /// Set of all KeyX values + /// Set of all partition keys /// - private readonly BigInteger[] KeyXMap = new BigInteger[8]; - - /// - /// Set of all KeyX2C values - /// - private readonly BigInteger[] KeyX2CMap = new BigInteger[8]; - - /// - /// Set of all KeyY values - /// - private readonly BigInteger[] KeyYMap = new BigInteger[8]; - - /// - /// Set of all KeyY values - /// - private readonly BigInteger[] NormalKeyMap = new BigInteger[8]; - - /// - /// Set of all KeyY values - /// - private readonly BigInteger[] NormalKey2CMap = new BigInteger[8]; + private readonly PartitionKeys[] KeysMap = new PartitionKeys[8]; public CIATool(bool development, DecryptArgs decryptArgs) { @@ -82,11 +62,11 @@ namespace NDecrypt.N3DS try { // Open the read and write on the same file for inplace processing - using var reader = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - using var writer = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); + using var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var output = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); // Deserialize the CIA information - var cia = ReadCIA(reader); + var cia = ReadCIA(input); if (cia == null) { Console.WriteLine("Error: Not a 3DS CIA!"); @@ -94,7 +74,7 @@ namespace NDecrypt.N3DS } // Process all NCCH partitions - ProcessAllPartitions(cia, encrypt, force, reader, writer); + ProcessAllPartitions(cia, encrypt, force, input, output); return false; } @@ -112,13 +92,13 @@ namespace NDecrypt.N3DS /// CIA representing the 3DS CIA file /// Indicates if the file should be encrypted or decrypted /// Indicates if the operation should be forced - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output private void ProcessAllPartitions(CIA cia, bool encrypt, bool force, - Stream reader, - Stream writer) + Stream input, + Stream output) { // Iterate over all NCCH partitions for (int p = 0; p < cia.Partitions!.Length; p++) @@ -127,7 +107,7 @@ namespace NDecrypt.N3DS if (ncchHeader == null) continue; - ProcessPartition(ncchHeader, p, encrypt, force, reader, writer); + ProcessPartition(ncchHeader, p, encrypt, force, input, output); } } @@ -138,14 +118,14 @@ namespace NDecrypt.N3DS /// Index of the partition /// Indicates if the file should be encrypted or decrypted /// Indicates if the operation should be forced - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output private void ProcessPartition(NCCHHeader ncchHeader, int partitionIndex, bool encrypt, bool force, - Stream reader, - Stream writer) + Stream input, + Stream output) { // If we're forcing the operation, tell the user if (force) @@ -166,22 +146,22 @@ namespace NDecrypt.N3DS SetEncryptionKeys(ncchHeader, partitionIndex, encrypt); // Process the extended header - ProcessExtendedHeader(ncchHeader, partitionIndex, tableEntry, encrypt, reader, writer); + ProcessExtendedHeader(ncchHeader, partitionIndex, tableEntry, encrypt, input, output); // If we're encrypting, encrypt the filesystems and update the flags if (encrypt) { - EncryptExeFS(ncchHeader, partitionIndex, tableEntry, reader, writer); - EncryptRomFS(ncchHeader, partitionIndex, tableEntry, reader, writer); - UpdateEncryptCryptoAndMasks(ncchHeader, partitionIndex, tableEntry, writer); + EncryptExeFS(ncchHeader, partitionIndex, tableEntry, input, output); + EncryptRomFS(ncchHeader, partitionIndex, tableEntry, input, output); + UpdateEncryptCryptoAndMasks(ncchHeader, partitionIndex, tableEntry, output); } // If we're decrypting, decrypt the filesystems and update the flags else { - DecryptExeFS(ncchHeader, partitionIndex, tableEntry, reader, writer); - DecryptRomFS(ncchHeader, partitionIndex, tableEntry, reader, writer); - UpdateDecryptCryptoAndMasks(ncchHeader, tableEntry, writer); + DecryptExeFS(ncchHeader, partitionIndex, tableEntry, input, output); + DecryptRomFS(ncchHeader, partitionIndex, tableEntry, input, output); + UpdateDecryptCryptoAndMasks(ncchHeader, tableEntry, output); } } @@ -193,17 +173,8 @@ namespace NDecrypt.N3DS /// Indicates if the file should be encrypted or decrypted private void SetEncryptionKeys(NCCHHeader ncchHeader, int partitionIndex, bool encrypt) { - KeyXMap[partitionIndex] = 0; - KeyX2CMap[partitionIndex] = _development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C; - - // Backup headers can't have a KeyY value set - if (ncchHeader.RSA2048Signature != null) - KeyYMap[partitionIndex] = new BigInteger(ncchHeader.RSA2048Signature.Take(16).Reverse().ToArray()); - else - KeyYMap[partitionIndex] = new BigInteger(0); - - NormalKeyMap[partitionIndex] = 0x00; - NormalKey2CMap[partitionIndex] = RotateLeft((RotateLeft(KeyX2CMap[partitionIndex], 2, 128) ^ KeyYMap[partitionIndex]) + _decryptArgs.AESHardwareConstant, 87, 128); + // Get partition-specific values + byte[]? rsaSignature = ncchHeader.RSA2048Signature; // TODO: Figure out what sane defaults for these values are // Set the header to use based on mode @@ -221,37 +192,8 @@ namespace NDecrypt.N3DS method = ncchHeader.Flags.CryptoMethod; } - if (masks.HasFlag(BitMasks.FixedCryptoKey)) - { - NormalKeyMap[partitionIndex] = 0x00; - NormalKey2CMap[partitionIndex] = 0x00; - Console.WriteLine("Encryption Method: Zero Key"); - } - else - { - if (method == CryptoMethod.Original) - { - KeyXMap[partitionIndex] = _development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C; - Console.WriteLine("Encryption Method: Key 0x2C"); - } - else if (method == CryptoMethod.Seven) - { - KeyXMap[partitionIndex] = _development ? _decryptArgs.DevKeyX0x25 : _decryptArgs.KeyX0x25; - Console.WriteLine("Encryption Method: Key 0x25"); - } - else if (method == CryptoMethod.NineThree) - { - KeyXMap[partitionIndex] = _development ? _decryptArgs.DevKeyX0x18 : _decryptArgs.KeyX0x18; - Console.WriteLine("Encryption Method: Key 0x18"); - } - else if (method == CryptoMethod.NineSix) - { - KeyXMap[partitionIndex] = _development ? _decryptArgs.DevKeyX0x1B : _decryptArgs.KeyX0x1B; - Console.WriteLine("Encryption Method: Key 0x1B"); - } - - NormalKeyMap[partitionIndex] = RotateLeft((RotateLeft(KeyXMap[partitionIndex], 2, 128) ^ KeyYMap[partitionIndex]) + _decryptArgs.AESHardwareConstant, 87, 128); - } + // Get the partition keys + KeysMap[partitionIndex] = new PartitionKeys(_decryptArgs, rsaSignature, masks, method, _development); } /// @@ -261,28 +203,28 @@ namespace NDecrypt.N3DS /// Index of the partition /// PartitionTableEntry header representing the partition /// Indicates if the file should be encrypted or decrypted - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output private bool ProcessExtendedHeader(NCCHHeader ncchHeader, int partitionIndex, PartitionTableEntry tableEntry, bool encrypt, - Stream reader, - Stream writer) + Stream input, + Stream output) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // mediaUnitSize; if (ncchHeader.ExtendedHeaderSizeInBytes > 0) { - reader.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin); - writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin); + input.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin); + output.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin); Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + ": ExHeader"); - var cipher = CreateAESCipher(NormalKey2CMap[partitionIndex], ncchHeader.PlainIV(), encrypt); - writer.Write(cipher.ProcessBytes(reader.ReadBytes(Constants.CXTExtendedDataHeaderLength))); - writer.Flush(); + var cipher = CreateAESCipher(KeysMap[partitionIndex].NormalKey2C, ncchHeader.PlainIV(), encrypt); + output.Write(cipher.ProcessBytes(input.ReadBytes(Constants.CXTExtendedDataHeaderLength))); + output.Flush(); return true; } else @@ -299,20 +241,20 @@ namespace NDecrypt.N3DS /// Index of the partition /// PartitionTableEntry header representing the partition /// Indicates if the file should be encrypted or decrypted - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output private void ProcessExeFSFileEntries(NCCHHeader ncchHeader, int partitionIndex, PartitionTableEntry tableEntry, bool encrypt, - Stream reader, - Stream writer) + Stream input, + Stream output) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // mediaUnitSize; - reader.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); - var exefsHeader = N3DSDeserializer.ParseExeFSHeader(reader); + input.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + var exefsHeader = N3DSDeserializer.ParseExeFSHeader(input); // If the header failed to read, log and return if (exefsHeader?.FileHeaders == null) @@ -333,26 +275,26 @@ namespace NDecrypt.N3DS byte[] exefsIVWithOffsetForHeader = AddToByteArray(ncchHeader.ExeFSIV(), (int)ctroffset); - var firstCipher = CreateAESCipher(NormalKeyMap[partitionIndex], exefsIVWithOffsetForHeader, encrypt); - var secondCipher = CreateAESCipher(NormalKey2CMap[partitionIndex], exefsIVWithOffsetForHeader, !encrypt); + var firstCipher = CreateAESCipher(KeysMap[partitionIndex].NormalKey, exefsIVWithOffsetForHeader, encrypt); + var secondCipher = CreateAESCipher(KeysMap[partitionIndex].NormalKey2C, exefsIVWithOffsetForHeader, !encrypt); - reader.Seek((((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); - writer.Seek((((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); + input.Seek((((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); + output.Seek((((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); if (datalenM > 0) { for (int i = 0; i < datalenM; i++) { - writer.Write(secondCipher.ProcessBytes(firstCipher.ProcessBytes(reader.ReadBytes(1024 * 1024)))); - writer.Flush(); + output.Write(secondCipher.ProcessBytes(firstCipher.ProcessBytes(input.ReadBytes(1024 * 1024)))); + output.Flush(); Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {i} / {datalenM + 1} mb..."); } } if (datalenB > 0) { - writer.Write(secondCipher.DoFinal(firstCipher.DoFinal(reader.ReadBytes((int)datalenB)))); - writer.Flush(); + output.Write(secondCipher.DoFinal(firstCipher.DoFinal(input.ReadBytes((int)datalenB)))); + output.Flush(); } Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n"); @@ -366,26 +308,26 @@ namespace NDecrypt.N3DS /// Index of the partition /// PartitionTableEntry header representing the partition /// Indicates if the file should be encrypted or decrypted - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output private void ProcessExeFSFilenameTable(NCCHHeader ncchHeader, int partitionIndex, PartitionTableEntry tableEntry, bool encrypt, - Stream reader, - Stream writer) + Stream input, + Stream output) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // mediaUnitSize; - reader.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); - writer.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + input.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + output.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table"); - var exeFSFilenameTable = CreateAESCipher(NormalKey2CMap[partitionIndex], ncchHeader.ExeFSIV(), encrypt); - writer.Write(exeFSFilenameTable.ProcessBytes(reader.ReadBytes((int)mediaUnitSize))); - writer.Flush(); + var exeFSFilenameTable = CreateAESCipher(KeysMap[partitionIndex].NormalKey2C, ncchHeader.ExeFSIV(), encrypt); + output.Write(exeFSFilenameTable.ProcessBytes(input.ReadBytes((int)mediaUnitSize))); + output.Flush(); } /// @@ -395,14 +337,14 @@ namespace NDecrypt.N3DS /// Index of the partition /// PartitionTableEntry header representing the partition /// Indicates if the file should be encrypted or decrypted - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output private void ProcessExeFS(NCCHHeader ncchHeader, int partitionIndex, PartitionTableEntry tableEntry, bool encrypt, - Stream reader, - Stream writer) + Stream input, + Stream output) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // mediaUnitSize; @@ -413,23 +355,23 @@ namespace NDecrypt.N3DS byte[] exefsIVWithOffset = AddToByteArray(ncchHeader.ExeFSIV(), ctroffsetE); - var exeFS = CreateAESCipher(NormalKey2CMap[partitionIndex], exefsIVWithOffset, encrypt); + var exeFS = CreateAESCipher(KeysMap[partitionIndex].NormalKey2C, exefsIVWithOffset, encrypt); - reader.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin); - writer.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin); + input.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin); + output.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin); if (exefsSizeM > 0) { for (int i = 0; i < exefsSizeM; i++) { - writer.Write(exeFS.ProcessBytes(reader.ReadBytes(1024 * 1024))); - writer.Flush(); + output.Write(exeFS.ProcessBytes(input.ReadBytes(1024 * 1024))); + output.Flush(); Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {i} / {exefsSizeM + 1} mb"); } } if (exefsSizeB > 0) { - writer.Write(exeFS.DoFinal(reader.ReadBytes(exefsSizeB))); - writer.Flush(); + output.Write(exeFS.DoFinal(input.ReadBytes(exefsSizeB))); + output.Flush(); } Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n"); @@ -445,13 +387,13 @@ namespace NDecrypt.N3DS /// NCCH header representing the partition /// Index of the partition /// PartitionTableEntry header representing the partition - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output private void DecryptExeFS(NCCHHeader ncchHeader, int partitionIndex, PartitionTableEntry tableEntry, - Stream reader, - Stream writer) + Stream input, + Stream output) { // If the ExeFS size is 0, we log and return if (ncchHeader.ExeFSSizeInMediaUnits == 0) @@ -461,14 +403,14 @@ namespace NDecrypt.N3DS } // Decrypt the filename table - ProcessExeFSFilenameTable(ncchHeader, partitionIndex, tableEntry, encrypt: false, reader, writer); + ProcessExeFSFilenameTable(ncchHeader, partitionIndex, tableEntry, encrypt: false, input, output); // For all but the original crypto method, process each of the files in the table if (ncchHeader.Flags!.CryptoMethod != CryptoMethod.Original) - ProcessExeFSFileEntries(ncchHeader, partitionIndex, tableEntry, encrypt: false, reader, writer); + ProcessExeFSFileEntries(ncchHeader, partitionIndex, tableEntry, encrypt: false, input, output); // Decrypt the rest of the ExeFS - ProcessExeFS(ncchHeader, partitionIndex, tableEntry, encrypt: false, reader, writer); + ProcessExeFS(ncchHeader, partitionIndex, tableEntry, encrypt: false, input, output); } /// @@ -477,14 +419,14 @@ namespace NDecrypt.N3DS /// NCCH header representing the partition /// Index of the partition /// PartitionTableEntry header representing the partition - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output /// TODO: See how much can be extracted into a common method with Encrypt private void DecryptRomFS(NCCHHeader ncchHeader, int partitionIndex, PartitionTableEntry tableEntry, - Stream reader, - Stream writer) + Stream input, + Stream output) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize; @@ -499,23 +441,23 @@ namespace NDecrypt.N3DS long romfsSizeM = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * mediaUnitSize) / (1024 * 1024)); int romfsSizeB = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * mediaUnitSize) % (1024 * 1024)); - var cipher = CreateAESCipher(NormalKeyMap[partitionIndex], ncchHeader.RomFSIV(), encrypt: false); + var cipher = CreateAESCipher(KeysMap[partitionIndex].NormalKey, ncchHeader.RomFSIV(), encrypt: false); - reader.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); - writer.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + input.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + output.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); if (romfsSizeM > 0) { for (int i = 0; i < romfsSizeM; i++) { - writer.Write(cipher.ProcessBytes(reader.ReadBytes(1024 * 1024))); - writer.Flush(); + output.Write(cipher.ProcessBytes(input.ReadBytes(1024 * 1024))); + output.Flush(); Console.Write($"\rPartition {partitionIndex} RomFS: Decrypting: {i} / {romfsSizeM + 1} mb"); } } if (romfsSizeB > 0) { - writer.Write(cipher.DoFinal(reader.ReadBytes(romfsSizeB))); - writer.Flush(); + output.Write(cipher.DoFinal(input.ReadBytes(romfsSizeB))); + output.Flush(); } Console.Write($"\rPartition {partitionIndex} RomFS: Decrypting: {romfsSizeM + 1} / {romfsSizeM + 1} mb... Done!\r\n"); @@ -526,26 +468,26 @@ namespace NDecrypt.N3DS /// /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// Stream representing the output + /// Stream representing the output private void UpdateDecryptCryptoAndMasks(NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - Stream writer) + Stream output) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize; // Write the new CryptoMethod - writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin); - writer.Write((byte)CryptoMethod.Original); - writer.Flush(); + output.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin); + output.Write((byte)CryptoMethod.Original); + output.Flush(); // Write the new BitMasks flag - writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin); + output.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin); BitMasks flag = ncchHeader.Flags!.BitMasks; flag &= (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF); flag |= BitMasks.NoCrypto; - writer.Write((byte)flag); - writer.Flush(); + output.Write((byte)flag); + output.Flush(); } #endregion @@ -558,13 +500,13 @@ namespace NDecrypt.N3DS /// NCCH header representing the partition /// Index of the partition /// PartitionTableEntry header representing the partition - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output private void EncryptExeFS(NCCHHeader ncchHeader, int partitionIndex, PartitionTableEntry tableEntry, - Stream reader, - Stream writer) + Stream input, + Stream output) { // If the ExeFS size is 0, we log and return if (ncchHeader.ExeFSSizeInMediaUnits == 0) @@ -579,10 +521,10 @@ namespace NDecrypt.N3DS // ProcessExeFSFileEntries(ncchHeader, reader, writer); // Encrypt the filename table - ProcessExeFSFilenameTable(ncchHeader, partitionIndex, tableEntry, encrypt: true, reader, writer); + ProcessExeFSFilenameTable(ncchHeader, partitionIndex, tableEntry, encrypt: true, input, output); // Encrypt the rest of the ExeFS - ProcessExeFS(ncchHeader, partitionIndex, tableEntry, encrypt: true, reader, writer); + ProcessExeFS(ncchHeader, partitionIndex, tableEntry, encrypt: true, input, output); } /// @@ -591,14 +533,14 @@ namespace NDecrypt.N3DS /// NCCH header representing the partition /// Index of the partition /// PartitionTableEntry header representing the partition - /// Stream representing the input - /// Stream representing the output + /// Stream representing the input + /// Stream representing the output /// TODO: See how much can be extracted into a common method with Decrypt private void EncryptRomFS(NCCHHeader ncchHeader, int partitionIndex, PartitionTableEntry tableEntry, - Stream reader, - Stream writer) + Stream input, + Stream output) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize; @@ -623,28 +565,28 @@ namespace NDecrypt.N3DS //} //else //{ - KeyXMap[partitionIndex] = (_development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C); - NormalKeyMap[partitionIndex] = RotateLeft((RotateLeft(KeyXMap[partitionIndex], 2, 128) ^ KeyYMap[partitionIndex]) + _decryptArgs.AESHardwareConstant, 87, 128); + KeysMap[partitionIndex].KeyX = (_development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C); + KeysMap[partitionIndex].NormalKey = RotateLeft((RotateLeft(KeysMap[partitionIndex].KeyX, 2, 128) ^ KeysMap[partitionIndex].KeyY) + _decryptArgs.AESHardwareConstant, 87, 128); //} } - var cipher = CreateAESCipher(NormalKeyMap[partitionIndex], ncchHeader.RomFSIV(), encrypt: true); + var cipher = CreateAESCipher(KeysMap[partitionIndex].NormalKey, ncchHeader.RomFSIV(), encrypt: true); - reader.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); - writer.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + input.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + output.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); if (romfsSizeM > 0) { for (int i = 0; i < romfsSizeM; i++) { - writer.Write(cipher.ProcessBytes(reader.ReadBytes(1024 * 1024))); - writer.Flush(); + output.Write(cipher.ProcessBytes(input.ReadBytes(1024 * 1024))); + output.Flush(); Console.Write($"\rPartition {partitionIndex} RomFS: Encrypting: {i} / {romfsSizeM + 1} mb"); } } if (romfsSizeB > 0) { - writer.Write(cipher.DoFinal(reader.ReadBytes(romfsSizeB))); - writer.Flush(); + output.Write(cipher.DoFinal(input.ReadBytes(romfsSizeB))); + output.Flush(); } Console.Write($"\rPartition {partitionIndex} RomFS: Encrypting: {romfsSizeM + 1} / {romfsSizeM + 1} mb... Done!\r\n"); @@ -656,38 +598,38 @@ namespace NDecrypt.N3DS /// NCCH header representing the partition /// Index of the partition /// PartitionTableEntry header representing the partition - /// Stream representing the output + /// Stream representing the output private void UpdateEncryptCryptoAndMasks(NCCHHeader ncchHeader, int partitionIndex, PartitionTableEntry tableEntry, - Stream writer) + Stream output) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize; // Write the new CryptoMethod - writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin); + output.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin); // For partitions 1 and up, set crypto-method to 0x00 if (partitionIndex > 0) - writer.Write((byte)CryptoMethod.Original); + output.Write((byte)CryptoMethod.Original); // TODO: Determine how to figure out the original crypto method, if possible // If partition 0, restore crypto-method from backup flags //else // writer.Write((byte)ciaHeader.BackupHeader.Flags.CryptoMethod); - writer.Flush(); + output.Flush(); // Write the new BitMasks flag - writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin); + output.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin); BitMasks flag = ncchHeader.Flags!.BitMasks; flag &= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF; // TODO: Determine how to figure out the original crypto method, if possible //flag |= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & ciaHeader.BackupHeader.Flags.BitMasks; - writer.Write((byte)flag); - writer.Flush(); + output.Write((byte)flag); + output.Flush(); } #endregion @@ -697,13 +639,13 @@ namespace NDecrypt.N3DS /// /// Read from a stream and get a CIA header, if possible /// - /// Stream representing the input + /// Stream representing the input /// CIA header object, null on error - private static CIA? ReadCIA(Stream reader) + private static CIA? ReadCIA(Stream input) { try { - return CIADeserializer.DeserializeStream(reader); + return CIADeserializer.DeserializeStream(input); } catch {