Move encrypt flag out of args

This commit is contained in:
Matt Nadareski
2024-10-12 00:59:07 -04:00
parent 84b62e2c52
commit 3f07271e7f
6 changed files with 189 additions and 122 deletions

View File

@@ -10,11 +10,6 @@ namespace NDecrypt.Core
{
#region Common Fields
/// <summary>
/// Flag to indicate operation
/// </summary>
public bool Encrypt { get; set; } = true;
/// <summary>
/// Flag to indicate forcing the operation
/// </summary>

View File

@@ -2,6 +2,16 @@
{
public interface ITool
{
bool ProcessFile();
/// <summary>
/// Attempts to encrypt an input file
/// </summary>
/// <returns>True if the file could be encrypted, false otherwise</returns>
bool EncryptFile();
/// <summary>
/// Attempts to decrypt an input file
/// </summary>
/// <returns>True if the file could be decrypted, false otherwise</returns>
bool DecryptFile();
}
}

View File

@@ -57,10 +57,17 @@ namespace NDecrypt.N3DS
#region Common Methods
/// <inheritdoc/>
public bool EncryptFile() => ProcessFile(encrypt: true);
/// <inheritdoc/>
public bool DecryptFile() => ProcessFile(encrypt: false);
/// <summary>
/// Process an input file given the input values
/// </summary>
public bool ProcessFile()
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
private bool ProcessFile(bool encrypt)
{
// Ensure the constants are all set
if (decryptArgs.IsReady != true)
@@ -84,7 +91,7 @@ namespace NDecrypt.N3DS
}
// Process all NCCH partitions
ProcessAllPartitions(cia, reader, writer);
ProcessAllPartitions(cia, encrypt, reader, writer);
return false;
}
@@ -100,9 +107,10 @@ namespace NDecrypt.N3DS
/// Process all partitions in the content file data of a CIA header
/// </summary>
/// <param name="cia">CIA representing the 3DS CIA file</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessAllPartitions(CIA cia, Stream reader, Stream writer)
private void ProcessAllPartitions(CIA cia, bool encrypt, Stream reader, Stream writer)
{
// Iterate over all NCCH partitions
for (int p = 0; p < cia.Partitions!.Length; p++)
@@ -111,19 +119,21 @@ namespace NDecrypt.N3DS
if (ncchHeader == null)
continue;
ProcessPartition(p, ncchHeader, reader, writer);
ProcessPartition(ncchHeader, p, encrypt, reader, writer);
}
}
/// <summary>
/// Process a single partition
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessPartition(int partitionIndex,
NCCHHeader ncchHeader,
private void ProcessPartition(NCCHHeader ncchHeader,
int partitionIndex,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -133,9 +143,9 @@ namespace NDecrypt.N3DS
Console.WriteLine($"Partition {partitionIndex} is not verified due to force flag being set.");
}
// If we're not forcing the operation, check if the 'NoCrypto' bit is set
else if (ncchHeader.Flags!.PossblyDecrypted() ^ decryptArgs.Encrypt)
else if (ncchHeader.Flags!.PossblyDecrypted() ^ encrypt)
{
Console.WriteLine($"Partition {partitionIndex}: Already " + (decryptArgs.Encrypt ? "Encrypted" : "Decrypted") + "?...");
Console.WriteLine($"Partition {partitionIndex}: Already " + (encrypt ? "Encrypted" : "Decrypted") + "?...");
return;
}
@@ -143,24 +153,24 @@ namespace NDecrypt.N3DS
var tableEntry = new PartitionTableEntry();
// Determine the Keys to be used
SetEncryptionKeys(partitionIndex, ncchHeader);
SetEncryptionKeys(ncchHeader, partitionIndex, encrypt);
// Process the extended header
ProcessExtendedHeader(partitionIndex, ncchHeader, tableEntry, reader, writer);
ProcessExtendedHeader(ncchHeader, partitionIndex, tableEntry, encrypt, reader, writer);
// If we're encrypting, encrypt the filesystems and update the flags
if (decryptArgs.Encrypt)
if (encrypt)
{
EncryptExeFS(partitionIndex, ncchHeader, tableEntry, reader, writer);
EncryptRomFS(partitionIndex, ncchHeader, tableEntry, reader, writer);
UpdateEncryptCryptoAndMasks(partitionIndex, ncchHeader, tableEntry, writer);
EncryptExeFS(ncchHeader, partitionIndex, tableEntry, reader, writer);
EncryptRomFS(ncchHeader, partitionIndex, tableEntry, reader, writer);
UpdateEncryptCryptoAndMasks(ncchHeader, partitionIndex, tableEntry, writer);
}
// If we're decrypting, decrypt the filesystems and update the flags
else
{
DecryptExeFS(partitionIndex, ncchHeader, tableEntry, reader, writer);
DecryptRomFS(partitionIndex, ncchHeader, tableEntry, reader, writer);
DecryptExeFS(ncchHeader, partitionIndex, tableEntry, reader, writer);
DecryptRomFS(ncchHeader, partitionIndex, tableEntry, reader, writer);
UpdateDecryptCryptoAndMasks(ncchHeader, tableEntry, writer);
}
}
@@ -168,9 +178,10 @@ namespace NDecrypt.N3DS
/// <summary>
/// Determine the set of keys to be used for encryption or decryption
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
private void SetEncryptionKeys(int partitionIndex, NCCHHeader ncchHeader)
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
private void SetEncryptionKeys(NCCHHeader ncchHeader, int partitionIndex, bool encrypt)
{
KeyX[partitionIndex] = 0;
KeyX2C[partitionIndex] = decryptArgs.Development ? decryptArgs.DevKeyX0x2C : decryptArgs.KeyX0x2C;
@@ -188,7 +199,7 @@ namespace NDecrypt.N3DS
// Set the header to use based on mode
BitMasks masks = BitMasks.NoCrypto;
CryptoMethod method = CryptoMethod.Original;
if (decryptArgs.Encrypt)
if (encrypt)
{
// TODO: Can we actually re-encrypt a CIA?
//masks = ciaHeader.BackupHeader.Flags.BitMasks;
@@ -236,14 +247,16 @@ namespace NDecrypt.N3DS
/// <summary>
/// Process the extended header, if it exists
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private bool ProcessExtendedHeader(int partitionIndex,
NCCHHeader ncchHeader,
private bool ProcessExtendedHeader(NCCHHeader ncchHeader,
int partitionIndex,
PartitionTableEntry tableEntry,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -255,9 +268,9 @@ namespace NDecrypt.N3DS
reader.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin);
writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin);
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], ncchHeader.PlainIV(), decryptArgs.Encrypt);
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], ncchHeader.PlainIV(), encrypt);
writer.Write(cipher.ProcessBytes(reader.ReadBytes(Constants.CXTExtendedDataHeaderLength)));
writer.Flush();
return true;
@@ -272,14 +285,16 @@ namespace NDecrypt.N3DS
/// <summary>
/// Process the extended header, if it exists
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessExeFSFileEntries(int partitionIndex,
NCCHHeader ncchHeader,
private void ProcessExeFSFileEntries(NCCHHeader ncchHeader,
int partitionIndex,
PartitionTableEntry tableEntry,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -308,8 +323,8 @@ namespace NDecrypt.N3DS
byte[] exefsIVWithOffsetForHeader = AddToByteArray(ncchHeader.ExeFSIV(), (int)ctroffset);
var firstCipher = CreateAESCipher(NormalKey[partitionIndex], exefsIVWithOffsetForHeader, decryptArgs.Encrypt);
var secondCipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffsetForHeader, !decryptArgs.Encrypt);
var firstCipher = CreateAESCipher(NormalKey[partitionIndex], exefsIVWithOffsetForHeader, encrypt);
var secondCipher = CreateAESCipher(NormalKey2C[partitionIndex], 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);
@@ -320,7 +335,7 @@ namespace NDecrypt.N3DS
{
writer.Write(secondCipher.ProcessBytes(firstCipher.ProcessBytes(reader.ReadBytes(1024 * 1024))));
writer.Flush();
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {i} / {datalenM + 1} mb...");
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {i} / {datalenM + 1} mb...");
}
}
@@ -330,21 +345,23 @@ namespace NDecrypt.N3DS
writer.Flush();
}
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n");
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n");
}
}
/// <summary>
/// Process the ExeFS Filename Table
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessExeFSFilenameTable(int partitionIndex,
NCCHHeader ncchHeader,
private void ProcessExeFSFilenameTable(NCCHHeader ncchHeader,
int partitionIndex,
PartitionTableEntry tableEntry,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -354,9 +371,9 @@ namespace NDecrypt.N3DS
reader.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
writer.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");
var exeFSFilenameTable = CreateAESCipher(NormalKey2C[partitionIndex], ncchHeader.ExeFSIV(), decryptArgs.Encrypt);
var exeFSFilenameTable = CreateAESCipher(NormalKey2C[partitionIndex], ncchHeader.ExeFSIV(), encrypt);
writer.Write(exeFSFilenameTable.ProcessBytes(reader.ReadBytes((int)mediaUnitSize)));
writer.Flush();
}
@@ -364,14 +381,16 @@ namespace NDecrypt.N3DS
/// <summary>
/// Process the ExeFS, if it exists
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessExeFS(int partitionIndex,
NCCHHeader ncchHeader,
private void ProcessExeFS(NCCHHeader ncchHeader,
int partitionIndex,
PartitionTableEntry tableEntry,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -384,7 +403,7 @@ namespace NDecrypt.N3DS
byte[] exefsIVWithOffset = AddToByteArray(ncchHeader.ExeFSIV(), ctroffsetE);
var exeFS = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffset, decryptArgs.Encrypt);
var exeFS = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffset, encrypt);
reader.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin);
writer.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin);
@@ -394,7 +413,7 @@ namespace NDecrypt.N3DS
{
writer.Write(exeFS.ProcessBytes(reader.ReadBytes(1024 * 1024)));
writer.Flush();
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {i} / {exefsSizeM + 1} mb");
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {i} / {exefsSizeM + 1} mb");
}
}
if (exefsSizeB > 0)
@@ -403,7 +422,7 @@ namespace NDecrypt.N3DS
writer.Flush();
}
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n");
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n");
}
#endregion
@@ -413,13 +432,13 @@ namespace NDecrypt.N3DS
/// <summary>
/// Decrypt the ExeFS, if it exists
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void DecryptExeFS(int partitionIndex,
NCCHHeader ncchHeader,
private void DecryptExeFS(NCCHHeader ncchHeader,
int partitionIndex,
PartitionTableEntry tableEntry,
Stream reader,
Stream writer)
@@ -432,27 +451,27 @@ namespace NDecrypt.N3DS
}
// Decrypt the filename table
ProcessExeFSFilenameTable(partitionIndex, ncchHeader, tableEntry, reader, writer);
ProcessExeFSFilenameTable(ncchHeader, partitionIndex, tableEntry, encrypt: false, reader, writer);
// For all but the original crypto method, process each of the files in the table
if (ncchHeader.Flags!.CryptoMethod != CryptoMethod.Original)
ProcessExeFSFileEntries(partitionIndex, ncchHeader, tableEntry, reader, writer);
ProcessExeFSFileEntries(ncchHeader, partitionIndex, tableEntry, encrypt: false, reader, writer);
// Decrypt the rest of the ExeFS
ProcessExeFS(partitionIndex, ncchHeader, tableEntry, reader, writer);
ProcessExeFS(ncchHeader, partitionIndex, tableEntry, encrypt: false, reader, writer);
}
/// <summary>
/// Decrypt the RomFS, if it exists
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
/// TODO: See how much can be extracted into a common method with Encrypt
private void DecryptRomFS(int partitionIndex,
NCCHHeader ncchHeader,
private void DecryptRomFS(NCCHHeader ncchHeader,
int partitionIndex,
PartitionTableEntry tableEntry,
Stream reader,
Stream writer)
@@ -470,7 +489,7 @@ namespace NDecrypt.N3DS
long romfsSizeM = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * mediaUnitSize) / (1024 * 1024));
int romfsSizeB = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * mediaUnitSize) % (1024 * 1024));
var cipher = CreateAESCipher(NormalKey[partitionIndex], ncchHeader.RomFSIV(), decryptArgs.Encrypt);
var cipher = CreateAESCipher(NormalKey[partitionIndex], ncchHeader.RomFSIV(), encrypt: false);
reader.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
writer.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
@@ -526,13 +545,13 @@ namespace NDecrypt.N3DS
/// <summary>
/// Encrypt the ExeFS, if it exists
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void EncryptExeFS(int partitionIndex,
NCCHHeader ncchHeader,
private void EncryptExeFS(NCCHHeader ncchHeader,
int partitionIndex,
PartitionTableEntry tableEntry,
Stream reader,
Stream writer)
@@ -550,23 +569,23 @@ namespace NDecrypt.N3DS
// ProcessExeFSFileEntries(ncchHeader, reader, writer);
// Encrypt the filename table
ProcessExeFSFilenameTable(partitionIndex, ncchHeader, tableEntry, reader, writer);
ProcessExeFSFilenameTable(ncchHeader, partitionIndex, tableEntry, encrypt: true, reader, writer);
// Encrypt the rest of the ExeFS
ProcessExeFS(partitionIndex, ncchHeader, tableEntry, reader, writer);
ProcessExeFS(ncchHeader, partitionIndex, tableEntry, encrypt: true, reader, writer);
}
/// <summary>
/// Encrypt the RomFS, if it exists
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
/// TODO: See how much can be extracted into a common method with Decrypt
private void EncryptRomFS(int partitionIndex,
NCCHHeader ncchHeader,
private void EncryptRomFS(NCCHHeader ncchHeader,
int partitionIndex,
PartitionTableEntry tableEntry,
Stream reader,
Stream writer)
@@ -599,7 +618,7 @@ namespace NDecrypt.N3DS
//}
}
var cipher = CreateAESCipher(NormalKey[partitionIndex], ncchHeader.RomFSIV(), decryptArgs.Encrypt);
var cipher = CreateAESCipher(NormalKey[partitionIndex], ncchHeader.RomFSIV(), encrypt: true);
reader.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
writer.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
@@ -624,12 +643,12 @@ namespace NDecrypt.N3DS
/// <summary>
/// Update the CryptoMethod and BitMasks for the encrypted partition
/// </summary>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="ncchHeader">NCCH header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="writer">Stream representing the output</param>
private void UpdateEncryptCryptoAndMasks(int partitionIndex,
NCCHHeader ncchHeader,
private void UpdateEncryptCryptoAndMasks(NCCHHeader ncchHeader,
int partitionIndex,
PartitionTableEntry tableEntry,
Stream writer)
{

View File

@@ -55,10 +55,17 @@ namespace NDecrypt.N3DS
#region Common Methods
/// <inheritdoc/>
public bool EncryptFile() => ProcessFile(encrypt: true);
/// <inheritdoc/>
public bool DecryptFile() => ProcessFile(encrypt: false);
/// <summary>
/// Process an input file given the input values
/// </summary>
public bool ProcessFile()
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
private bool ProcessFile(bool encrypt)
{
// Ensure the constants are all set
if (decryptArgs.IsReady != true)
@@ -82,7 +89,7 @@ namespace NDecrypt.N3DS
}
// Process all 8 NCCH partitions
ProcessAllPartitions(cart, reader, writer);
ProcessAllPartitions(cart, encrypt, reader, writer);
return true;
}
@@ -98,9 +105,10 @@ namespace NDecrypt.N3DS
/// Process all partitions in the partition table of an NCSD header
/// </summary>
/// <param name="cart">Cart representing the 3DS file</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessAllPartitions(Cart cart, Stream reader, Stream writer)
private void ProcessAllPartitions(Cart cart, bool encrypt, Stream reader, Stream writer)
{
// Check the partitions table
if (cart.Header?.PartitionsTable == null)
@@ -119,7 +127,7 @@ namespace NDecrypt.N3DS
continue;
}
ProcessPartition(cart, partitionIndex, reader, writer);
ProcessPartition(cart, partitionIndex, encrypt, reader, writer);
}
}
@@ -128,10 +136,12 @@ namespace NDecrypt.N3DS
/// </summary>
/// <param name="cart">Cart representing the 3DS file</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessPartition(Cart cart,
int partitionIndex,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -141,20 +151,20 @@ namespace NDecrypt.N3DS
Console.WriteLine($"Partition {partitionIndex} is not verified due to force flag being set.");
}
// If we're not forcing the operation, check if the 'NoCrypto' bit is set
else if (cart.Partitions![partitionIndex]!.Flags!.PossblyDecrypted() ^ decryptArgs.Encrypt)
else if (cart.Partitions![partitionIndex]!.Flags!.PossblyDecrypted() ^ encrypt)
{
Console.WriteLine($"Partition {partitionIndex}: Already " + (decryptArgs.Encrypt ? "Encrypted" : "Decrypted") + "?...");
Console.WriteLine($"Partition {partitionIndex}: Already " + (encrypt ? "Encrypted" : "Decrypted") + "?...");
return;
}
// Determine the Keys to be used
SetEncryptionKeys(cart, partitionIndex);
SetEncryptionKeys(cart, partitionIndex, encrypt);
// Process the extended header
ProcessExtendedHeader(cart, partitionIndex, reader, writer);
ProcessExtendedHeader(cart, partitionIndex, encrypt, reader, writer);
// If we're encrypting, encrypt the filesystems and update the flags
if (decryptArgs.Encrypt)
if (encrypt)
{
EncryptExeFS(cart, partitionIndex, reader, writer);
EncryptRomFS(cart, partitionIndex, reader, writer);
@@ -175,7 +185,8 @@ namespace NDecrypt.N3DS
/// </summary>
/// <param name="cart">Cart representing the 3DS file</param>
/// <param name="partitionIndex">Index of the partition</param>
private void SetEncryptionKeys(Cart cart, int partitionIndex)
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
private void SetEncryptionKeys(Cart cart, int partitionIndex, bool encrypt)
{
// Get the backup header
var backupHeader = cart.CardInfoHeader!.InitialData!.BackupHeader;
@@ -196,7 +207,7 @@ namespace NDecrypt.N3DS
// Set the header to use based on mode
BitMasks masks;
CryptoMethod method;
if (decryptArgs.Encrypt)
if (encrypt)
{
masks = backupHeader!.Flags!.BitMasks;
method = backupHeader.Flags.CryptoMethod;
@@ -245,10 +256,12 @@ namespace NDecrypt.N3DS
/// </summary>
/// <param name="cart">Cart representing the 3DS file</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private bool ProcessExtendedHeader(Cart cart,
int partitionIndex,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -262,10 +275,10 @@ namespace NDecrypt.N3DS
reader.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
writer.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
// Create the Plain AES cipher for this partition
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], cart.PlainIV(partitionIndex), decryptArgs.Encrypt);
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], cart.PlainIV(partitionIndex), encrypt);
// Process the extended header
byte[] readBytes = reader.ReadBytes(Constants.CXTExtendedDataHeaderLength);
@@ -290,11 +303,13 @@ namespace NDecrypt.N3DS
/// Process the extended header, if it exists
/// </summary>
/// <param name="cart">Cart representing the 3DS file</param>
/// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessExeFSFileEntries(Cart cart,
int partitionIndex,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -327,8 +342,8 @@ namespace NDecrypt.N3DS
// Create the ExeFS AES ciphers for this partition
byte[] exefsIVWithOffsetForHeader = AddToByteArray(cart.ExeFSIV(partitionIndex), (int)ctroffset);
var firstCipher = CreateAESCipher(NormalKey[partitionIndex], exefsIVWithOffsetForHeader, decryptArgs.Encrypt);
var secondCipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffsetForHeader, !decryptArgs.Encrypt);
var firstCipher = CreateAESCipher(NormalKey[partitionIndex], exefsIVWithOffsetForHeader, encrypt);
var secondCipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffsetForHeader, !encrypt);
// Seek to the file entry
reader.Seek(exeFsOffset + fileHeader.FileOffset, SeekOrigin.Begin);
@@ -344,7 +359,7 @@ namespace NDecrypt.N3DS
byte[] secondProcessedBytes = secondCipher.ProcessBytes(firstProcessedBytes);
writer.Write(secondProcessedBytes);
writer.Flush();
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {i} / {datalenM + 1} mb...");
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {i} / {datalenM + 1} mb...");
}
}
@@ -358,7 +373,7 @@ namespace NDecrypt.N3DS
writer.Flush();
}
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n");
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.FileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n");
}
}
@@ -367,10 +382,12 @@ namespace NDecrypt.N3DS
/// </summary>
/// <param name="cart">Cart representing the 3DS file</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessExeFSFilenameTable(Cart cart,
int partitionIndex,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -383,10 +400,10 @@ namespace NDecrypt.N3DS
reader.Seek(exeFsHeaderOffset, SeekOrigin.Begin);
writer.Seek(exeFsHeaderOffset, SeekOrigin.Begin);
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");
// Create the ExeFS AES cipher for this partition
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], cart.ExeFSIV(partitionIndex), decryptArgs.Encrypt);
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], cart.ExeFSIV(partitionIndex), encrypt);
// Process the filename table
byte[] readBytes = reader.ReadBytes((int)cart.MediaUnitSize());
@@ -405,10 +422,12 @@ namespace NDecrypt.N3DS
/// </summary>
/// <param name="cart">Cart representing the 3DS file</param>
/// <param name="partitionIndex">Index of the partition</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private bool ProcessExeFS(Cart cart,
int partitionIndex,
bool encrypt,
Stream reader,
Stream writer)
{
@@ -432,7 +451,7 @@ namespace NDecrypt.N3DS
// Create the ExeFS AES cipher for this partition
byte[] exefsIVWithOffset = AddToByteArray(cart.ExeFSIV(partitionIndex), ctroffsetE);
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffset, decryptArgs.Encrypt);
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffset, encrypt);
// Seek to the ExeFS
reader.Seek(exeFsOffset, SeekOrigin.Begin);
@@ -447,7 +466,7 @@ namespace NDecrypt.N3DS
byte[] processedBytes = cipher.ProcessBytes(readBytes);
writer.Write(processedBytes);
writer.Flush();
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {i} / {exefsSizeM + 1} mb");
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {i} / {exefsSizeM + 1} mb");
}
}
@@ -460,7 +479,7 @@ namespace NDecrypt.N3DS
writer.Flush();
}
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n");
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n");
return true;
}
@@ -488,14 +507,14 @@ namespace NDecrypt.N3DS
}
// Decrypt the filename table
ProcessExeFSFilenameTable(cart, partitionIndex, reader, writer);
ProcessExeFSFilenameTable(cart, partitionIndex, encrypt: false, reader, writer);
// For all but the original crypto method, process each of the files in the table
if (cart.Partitions![partitionIndex]!.Flags!.CryptoMethod != CryptoMethod.Original)
ProcessExeFSFileEntries(cart, partitionIndex, reader, writer);
ProcessExeFSFileEntries(cart, partitionIndex, encrypt: false, reader, writer);
// Decrypt the rest of the ExeFS
ProcessExeFS(cart, partitionIndex, reader, writer);
ProcessExeFS(cart, partitionIndex, encrypt: false, reader, writer);
}
/// <summary>
@@ -529,7 +548,7 @@ namespace NDecrypt.N3DS
int romfsSizeB = (int)((long)romFsSize % (1024 * 1024));
// Create the RomFS AES cipher for this partition
var cipher = CreateAESCipher(NormalKey[partitionIndex], cart.RomFSIV(partitionIndex), decryptArgs.Encrypt);
var cipher = CreateAESCipher(NormalKey[partitionIndex], cart.RomFSIV(partitionIndex), encrypt: false);
// Seek to the RomFS
reader.Seek(romFsOffset, SeekOrigin.Begin);
@@ -619,13 +638,13 @@ namespace NDecrypt.N3DS
// For all but the original crypto method, process each of the files in the table
if (backupHeader!.Flags!.CryptoMethod != CryptoMethod.Original)
ProcessExeFSFileEntries(cart, partitionIndex, reader, writer);
ProcessExeFSFileEntries(cart, partitionIndex, encrypt: true, reader, writer);
// Encrypt the filename table
ProcessExeFSFilenameTable(cart, partitionIndex, reader, writer);
ProcessExeFSFilenameTable(cart, partitionIndex, encrypt: true, reader, writer);
// Encrypt the rest of the ExeFS
ProcessExeFS(cart, partitionIndex, reader, writer);
ProcessExeFS(cart, partitionIndex, encrypt: true, reader, writer);
}
/// <summary>
@@ -677,7 +696,7 @@ namespace NDecrypt.N3DS
}
// Create the RomFS AES cipher for this partition
var cipher = CreateAESCipher(NormalKey[partitionIndex], cart.RomFSIV(partitionIndex), decryptArgs.Encrypt);
var cipher = CreateAESCipher(NormalKey[partitionIndex], cart.RomFSIV(partitionIndex), encrypt: true);
// Seek to the RomFS
reader.Seek(romFsOffset, SeekOrigin.Begin);

View File

@@ -33,10 +33,17 @@ namespace NDecrypt.Nitro
this.decryptArgs = decryptArgs;
}
/// <inheritdoc/>
public bool EncryptFile() => ProcessFile(encrypt: true);
/// <inheritdoc/>
public bool DecryptFile() => ProcessFile(encrypt: false);
/// <summary>
/// Process an input file given the input values
/// </summary>
public bool ProcessFile()
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
private bool ProcessFile(bool encrypt)
{
try
{
@@ -53,7 +60,7 @@ namespace NDecrypt.Nitro
}
// Process the secure area
ProcessSecureArea(cart, reader, writer);
ProcessSecureArea(cart, encrypt, reader, writer);
return true;
}
@@ -69,9 +76,10 @@ namespace NDecrypt.Nitro
/// Process secure area in the DS/DSi file
/// </summary>s
/// <param name="cart">Cart representing the DS file</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessSecureArea(Cart cart, Stream reader, Stream writer)
private void ProcessSecureArea(Cart cart, bool encrypt, Stream reader, Stream writer)
{
// If we're forcing the operation, tell the user
if (decryptArgs.Force)
@@ -87,16 +95,16 @@ namespace NDecrypt.Nitro
Console.WriteLine("File has an empty secure area, cannot proceed");
return;
}
else if (decryptArgs.Encrypt ^ isDecrypted.Value)
else if (encrypt ^ isDecrypted.Value)
{
Console.WriteLine("File is already " + (decryptArgs.Encrypt ? "encrypted" : "decrypted"));
Console.WriteLine("File is already " + (encrypt ? "encrypted" : "decrypted"));
return;
}
}
ProcessARM9(cart.CommonHeader!, reader, writer);
ProcessARM9(cart.CommonHeader!, encrypt, reader, writer);
Console.WriteLine("File has been " + (decryptArgs.Encrypt ? "encrypted" : "decrypted"));
Console.WriteLine("File has been " + (encrypt ? "encrypted" : "decrypted"));
}
/// <summary>
@@ -162,9 +170,10 @@ namespace NDecrypt.Nitro
/// Process the secure ARM9 region of the file, if possible
/// </summary>
/// <param name="commonHeader">CommonHeader representing the DS header</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="reader">Stream representing the input</param>
/// <param name="writer">Stream representing the output</param>
private void ProcessARM9(CommonHeader commonHeader, Stream reader, Stream writer)
private void ProcessARM9(CommonHeader commonHeader, bool encrypt, Stream reader, Stream writer)
{
// Seek to the beginning of the secure area
reader.Seek(0x4000, SeekOrigin.Begin);
@@ -176,13 +185,13 @@ namespace NDecrypt.Nitro
// Perform the initialization steps
Init1(commonHeader);
if (!decryptArgs.Encrypt) Decrypt(ref p1, ref p0);
if (!encrypt) Decrypt(ref p1, ref p0);
_arg2[1] <<= 1;
_arg2[2] >>= 1;
Init2();
// If we're decrypting, set the proper flags
if (!decryptArgs.Encrypt)
if (!encrypt)
{
Decrypt(ref p1, ref p0);
if (p0 == Constants.MAGIC30 && p1 == Constants.MAGIC34)
@@ -206,7 +215,7 @@ namespace NDecrypt.Nitro
p0 = reader.ReadUInt32();
p1 = reader.ReadUInt32();
if (decryptArgs.Encrypt)
if (encrypt)
Encrypt(ref p1, ref p0);
else
Decrypt(ref p1, ref p0);
@@ -218,7 +227,7 @@ namespace NDecrypt.Nitro
}
// Replace the header explicitly if we're encrypting
if (decryptArgs.Encrypt)
if (encrypt)
{
reader.Seek(0x4000, SeekOrigin.Begin);
writer.Seek(0x4000, SeekOrigin.Begin);

View File

@@ -30,14 +30,15 @@ namespace NDecrypt
return;
}
bool encrypt;
var decryptArgs = new DecryptArgs();
if (args[0] == "decrypt" || args[0] == "d")
{
decryptArgs.Encrypt = false;
encrypt = false;
}
else if (args[0] == "encrypt" || args[0] == "e")
{
decryptArgs.Encrypt = true;
encrypt = true;
}
else
{
@@ -113,13 +114,13 @@ namespace NDecrypt
{
if (File.Exists(args[i]))
{
ProcessPath(args[i], decryptArgs, outputHashes);
ProcessPath(args[i], encrypt, decryptArgs, outputHashes);
}
else if (Directory.Exists(args[i]))
{
foreach (string file in Directory.EnumerateFiles(args[i], "*", SearchOption.AllDirectories))
{
ProcessPath(file, decryptArgs, outputHashes);
ProcessPath(file, encrypt, decryptArgs, outputHashes);
}
}
else
@@ -133,15 +134,29 @@ namespace NDecrypt
/// Display a basic help text
/// </summary>
/// <param name="path">Path to the file to process</param>
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
/// <param name="decryptArgs">DecryptArgs to use during processing</param>
/// <param name="outputHashes">True to write out a hashfile, false otherwise</param>
private static void ProcessPath(string path, DecryptArgs decryptArgs, bool outputHashes)
private static void ProcessPath(string path, bool encrypt, DecryptArgs decryptArgs, bool outputHashes)
{
Console.WriteLine(path);
ITool? tool = DeriveTool(path, decryptArgs);
if (tool?.ProcessFile() != true)
Console.WriteLine("Processing failed!");
else if (outputHashes)
if (tool == null)
return;
if (encrypt && !tool.EncryptFile())
{
Console.WriteLine("Encryption failed!");
return;
}
else if (!encrypt && !tool.DecryptFile())
{
Console.WriteLine("Decryption failed!");
return;
}
if (outputHashes)
WriteHashes(path);
}