diff --git a/3DSDecrypt/Headers/NCCHHeader.cs b/3DSDecrypt/Headers/NCCHHeader.cs index f892766..aa91466 100644 --- a/3DSDecrypt/Headers/NCCHHeader.cs +++ b/3DSDecrypt/Headers/NCCHHeader.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.IO; using System.Linq; using ThreeDS.Data; @@ -13,6 +12,9 @@ namespace ThreeDS.Headers public uint ContentSizeInMediaUnits; public uint ContentSizeInBytes { get { return ContentSizeInMediaUnits * 0x200; } } public byte[] PartitionId = new byte[8]; + public byte[] PlainIV { get { return PartitionId.Concat(Constants.PlainCounter).ToArray(); } } + public byte[] ExeFSIV { get { return PartitionId.Concat(Constants.ExefsCounter).ToArray(); } } + public byte[] RomFSIV { get { return PartitionId.Concat(Constants.RomfsCounter).ToArray(); } } public byte[] MakerCode = new byte[2]; public byte[] Version = new byte[2]; public byte[] VerificationHash = new byte[4]; diff --git a/3DSDecrypt/ThreeDSTool.cs b/3DSDecrypt/ThreeDSTool.cs index 8d2ab0f..4bac69e 100644 --- a/3DSDecrypt/ThreeDSTool.cs +++ b/3DSDecrypt/ThreeDSTool.cs @@ -11,7 +11,14 @@ namespace ThreeDS { public class ThreeDSTool { + /// + /// Name of the input 3DS file + /// private readonly string filename; + + /// + /// Flag to detrmine if development keys should be used + /// private readonly bool development; public ThreeDSTool(string filename, bool development) @@ -20,6 +27,9 @@ namespace ThreeDS this.development = development; } + /// + /// Attempt to decrypt a 3DS file + /// public void Decrypt() { if (!File.Exists(filename)) @@ -42,7 +52,7 @@ namespace ThreeDS { if (!header.PartitionsTable[p].IsValid()) { - Console.WriteLine("Partition {0} Not found... Skipping...", p); + Console.WriteLine($"Partition {p} Not found... Skipping..."); continue; } @@ -52,66 +62,20 @@ namespace ThreeDS NCCHHeader partitionHeader = NCCHHeader.Read(f); if (partitionHeader == null) { - Console.WriteLine("Partition {0} Unable to read NCCH header", p); + Console.WriteLine($"Partition {p} Unable to read NCCH header"); continue; } // Check if the 'NoCrypto' bit is set if ((partitionHeader.Flags.BitMasks & BitMasks.NoCrypto) != 0) { - Console.WriteLine("Partition {0:d}: Already Decrypted?...", p); + Console.WriteLine($"Partition {p}: Already Decrypted?..."); continue; } - // PartitionID is used as IV joined with the content type. - byte[] plainIV = partitionHeader.PartitionId.Concat(Constants.PlainCounter).ToArray(); // Get the IV for plain sector (TitleID + Plain Counter) - byte[] exefsIV = partitionHeader.PartitionId.Concat(Constants.ExefsCounter).ToArray(); // Get the IV for ExeFS (TitleID + ExeFS Counter) - byte[] romfsIV = partitionHeader.PartitionId.Concat(Constants.RomfsCounter).ToArray(); // Get the IV for RomFS (TitleID + RomFS Counter) - - BigInteger KeyX = 0; - BigInteger KeyX2C = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C); - BigInteger KeyY = new BigInteger(partitionHeader.RSA2048Signature.Take(16).Reverse().ToArray()); // KeyY is the first 16 bytes of the partition RSA-2048 SHA-256 signature - - BigInteger NormalKey = 0; - BigInteger NormalKey2C = RotateLeft((RotateLeft(KeyX2C, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128); - // Determine the Keys to be used - if ((partitionHeader.Flags.BitMasks & BitMasks.FixedCryptoKey) != 0) - { - NormalKey = 0x00; - NormalKey2C = 0x00; - if (p == 0) - Console.WriteLine("Encryption Method: Zero Key"); - } - else - { - if (partitionHeader.Flags.CryptoMethod == CryptoMethod.Original) - { - KeyX = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C); - if (p == 0) - Console.WriteLine("Encryption Method: Key 0x2C"); - } - else if (partitionHeader.Flags.CryptoMethod == CryptoMethod.Seven) - { - KeyX = (development ? Constants.KeyX0x25 : Constants.KeyX0x25); - if (p == 0) - Console.WriteLine("Encryption Method: Key 0x25"); - } - else if (partitionHeader.Flags.CryptoMethod == CryptoMethod.NineThree) - { - KeyX = (development ? Constants.DevKeyX0x18 : Constants.KeyX0x18); - if (p == 0) - Console.WriteLine("Encryption Method: Key 0x18"); - } - else if (partitionHeader.Flags.CryptoMethod == CryptoMethod.NineSix) - { - KeyX = (development ? Constants.DevKeyX0x1B : Constants.KeyX0x1B); - if (p == 0) - Console.WriteLine("Encryption Method: Key 0x1B"); - } - - NormalKey = RotateLeft((RotateLeft(KeyX, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128); - } + GetEncryptionKeys(partitionHeader.RSA2048Signature, partitionHeader.Flags.BitMasks, partitionHeader.Flags.CryptoMethod, p, + out BigInteger KeyX, out BigInteger KeyX2C, out BigInteger KeyY, out BigInteger NormalKey, out BigInteger NormalKey2C); // Decrypted extended header, if it exists if (partitionHeader.ExtendedHeaderSizeInBytes > 0) @@ -120,12 +84,10 @@ namespace ThreeDS f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin); g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin); - var str = BitConverter.ToString(plainIV).Replace("-", ""); - var exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); - exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), plainIV)); + exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), partitionHeader.PlainIV)); - Console.WriteLine("Partition {0} ExeFS: Decrypting: ExHeader", p); + Console.WriteLine($"Partition {p} ExeFS: Decrypting: ExHeader"); g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(Constants.CXTExtendedDataHeaderLength))); g.Flush(); @@ -138,12 +100,12 @@ namespace ThreeDS g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin); var exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); - exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIV)); + exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), partitionHeader.ExeFSIV)); g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes((int)header.SectorSize))); g.Flush(); - Console.WriteLine("Partition {0} ExeFS: Decrypting: ExeFS Filename Table", p); + Console.WriteLine($"Partition {p} ExeFS: Decrypting: ExeFS Filename Table"); if (partitionHeader.Flags.CryptoMethod != CryptoMethod.Original) { @@ -160,7 +122,7 @@ namespace ThreeDS uint datalenB = ((fileHeader.FileSize) % (1024 * 1024)); uint ctroffset = ((fileHeader.FileOffset + header.SectorSize) / 0x10); - byte[] exefsIVWithOffsetForHeader = AddToByteArray(exefsIV, (int)ctroffset); + byte[] exefsIVWithOffsetForHeader = AddToByteArray(partitionHeader.ExeFSIV, (int)ctroffset); var exefsctrmode = CipherUtilities.GetCipher("AES/CTR"); exefsctrmode.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), exefsIVWithOffsetForHeader)); @@ -177,7 +139,7 @@ namespace ThreeDS { g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes(1024 * 1024)))); g.Flush(); - Console.Write("\rPartition {0} ExeFS: Decrypting: {1}... {2} / {3} mb...", p, fileHeader.ReadableFileName, i, datalenM + 1); + Console.Write($"\rPartition {p} ExeFS: Decrypting: {fileHeader.ReadableFileName}... {i} / {datalenM + 1} mb..."); } } @@ -187,7 +149,7 @@ namespace ThreeDS g.Flush(); } - Console.Write("\rPartition {0} ExeFS: Decrypting: {1}... {2} / {3} mb... Done!\r\n", p, fileHeader.ReadableFileName, datalenM + 1, datalenM + 1); + Console.Write($"\rPartition {p} ExeFS: Decrypting: {fileHeader.ReadableFileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n"); } } } @@ -197,7 +159,7 @@ namespace ThreeDS int exefsSizeB = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.SectorSize) % (1024 * 1024); int ctroffsetE = (int)(header.SectorSize / 0x10); - byte[] exefsIVWithOffset = AddToByteArray(exefsIV, ctroffsetE); + byte[] exefsIVWithOffset = AddToByteArray(partitionHeader.ExeFSIV, ctroffsetE); exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffset)); @@ -210,7 +172,7 @@ namespace ThreeDS { g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(1024 * 1024))); g.Flush(); - Console.Write("\rPartition {0} ExeFS: Decrypting: {1} / {2} mb", p, i, exefsSizeM + 1); + Console.Write($"\rPartition {p} ExeFS: Decrypting: {i} / {exefsSizeM + 1} mb"); } } if (exefsSizeB > 0) @@ -219,11 +181,11 @@ namespace ThreeDS g.Flush(); } - Console.Write("\rPartition {0} ExeFS: Decrypting: {1} / {2} mb... Done!\r\n", p, exefsSizeM + 1, exefsSizeM + 1); + Console.Write($"\rPartition {p} ExeFS: Decrypting: {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n"); } else { - Console.WriteLine("Partition {0} ExeFS: No Data... Skipping...", p); + Console.WriteLine($"Partition {p} ExeFS: No Data... Skipping..."); } if (partitionHeader.RomFSOffsetInMediaUnits != 0) @@ -232,7 +194,7 @@ namespace ThreeDS int romfsSizeB = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) % (1024 * 1024); var romfsctrmode = CipherUtilities.GetCipher("AES/CTR"); - romfsctrmode.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), romfsIV)); + romfsctrmode.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), partitionHeader.RomFSIV)); f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin); g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin); @@ -242,7 +204,7 @@ namespace ThreeDS { g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(1024 * 1024))); g.Flush(); - Console.Write("\rPartition {0} RomFS: Decrypting: {1} / {2} mb", p, i, romfsSizeM + 1); + Console.Write($"\rPartition {p} RomFS: Decrypting: {i} / {romfsSizeM + 1} mb"); } } if (romfsSizeB > 0) @@ -251,11 +213,11 @@ namespace ThreeDS g.Flush(); } - Console.Write("\rPartition {0} RomFS: Decrypting: {1} / {2} mb... Done!\r\n", p, romfsSizeM + 1, romfsSizeM + 1); + Console.Write($"\rPartition {p} RomFS: Decrypting: {romfsSizeM + 1} / {romfsSizeM + 1} mb... Done!\r\n"); } else { - Console.WriteLine("Partition {0} RomFS: No Data... Skipping...", p); + Console.WriteLine($"Partition {p} RomFS: No Data... Skipping..."); } // Write the new CryptoMethod @@ -277,6 +239,9 @@ namespace ThreeDS } } + /// + /// Attempt to encrypt a 3DS file + /// public void Encrypt() { if (!File.Exists(filename)) @@ -294,12 +259,16 @@ namespace ThreeDS return; } + // Get the backup flags + f.BaseStream.Seek(0x1188, SeekOrigin.Begin); + NCCHHeaderFlags backupFlags = NCCHHeaderFlags.Read(f); + // Iterate over all 8 NCCH partitions for (int p = 0; p < 8; p++) { if (!header.PartitionsTable[p].IsValid()) { - Console.WriteLine("Partition {0} Not found... Skipping...", p); + Console.WriteLine($"Partition {p} Not found... Skipping..."); continue; } @@ -309,70 +278,20 @@ namespace ThreeDS NCCHHeader partitionHeader = NCCHHeader.Read(f); if (partitionHeader == null) { - Console.WriteLine("Partition {0} Unable to read NCCH header", p); + Console.WriteLine($"Partition {p} Unable to read NCCH header"); continue; } - // Get the backup flags - f.BaseStream.Seek(0x1188, SeekOrigin.Begin); - NCCHHeaderFlags backupFlags = NCCHHeaderFlags.Read(f); - // Check if the 'NoCrypto' bit is not set if ((partitionHeader.Flags.BitMasks & BitMasks.NoCrypto) == 0) { - Console.WriteLine("Partition {0:d}: Already Encrypted?...", p); + Console.WriteLine($"Partition {p}: Already Encrypted?..."); continue; } - // PartitionID is used as IV joined with the content type. - byte[] plainIV = partitionHeader.PartitionId.Concat(Constants.PlainCounter).ToArray(); // Get the IV for plain sector (TitleID + Plain Counter) - byte[] exefsIV = partitionHeader.PartitionId.Concat(Constants.ExefsCounter).ToArray(); // Get the IV for ExeFS (TitleID + ExeFS Counter) - byte[] romfsIV = partitionHeader.PartitionId.Concat(Constants.RomfsCounter).ToArray(); // Get the IV for RomFS (TitleID + RomFS Counter) - - BigInteger KeyX = 0; - BigInteger KeyX2C = Constants.KeyX0x2C; - BigInteger KeyY = new BigInteger(partitionHeader.RSA2048Signature.Take(16).Reverse().ToArray()); // KeyY is the first 16 bytes of the partition RSA-2048 SHA-256 signature - - BigInteger NormalKey = 0; - BigInteger NormalKey2C = RotateLeft((RotateLeft(KeyX2C, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128); - // Determine the Keys to be used - if ((backupFlags.BitMasks & BitMasks.FixedCryptoKey) != 0) - { - NormalKey = 0x00; - NormalKey2C = 0x00; - if (p == 0) - Console.WriteLine("Encryption Method: Zero Key"); - } - else - { - if (backupFlags.CryptoMethod == CryptoMethod.Original) - { - KeyX = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C); - if (p == 0) - Console.WriteLine("Encryption Method: Key 0x2C"); - } - else if (backupFlags.CryptoMethod == CryptoMethod.Seven) - { - KeyX = (development ? Constants.KeyX0x25 : Constants.KeyX0x25); - if (p == 0) - Console.WriteLine("Encryption Method: Key 0x25"); - } - else if (backupFlags.CryptoMethod == CryptoMethod.NineThree) - { - KeyX = (development ? Constants.DevKeyX0x18 : Constants.KeyX0x18); - if (p == 0) - Console.WriteLine("Encryption Method: Key 0x18"); - } - else if (backupFlags.CryptoMethod == CryptoMethod.NineSix) - { - KeyX = (development ? Constants.DevKeyX0x1B : Constants.KeyX0x1B); - if (p == 0) - Console.WriteLine("Encryption Method: Key 0x1B"); - } - - NormalKey = RotateLeft((RotateLeft(KeyX, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128); - } + GetEncryptionKeys(partitionHeader.RSA2048Signature, backupFlags.BitMasks, backupFlags.CryptoMethod, p, + out BigInteger KeyX, out BigInteger KeyX2C, out BigInteger KeyY, out BigInteger NormalKey, out BigInteger NormalKey2C); // Encrypt extended header, if it exists if (partitionHeader.ExtendedHeaderSizeInBytes > 0) @@ -381,12 +300,10 @@ namespace ThreeDS f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin); g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin); - var str = BitConverter.ToString(plainIV).Replace("-", ""); - var exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); - exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), plainIV)); + exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), partitionHeader.PlainIV)); - Console.WriteLine("Partition {0} ExeFS: Encrypting: ExHeader", p); + Console.WriteLine($"Partition {p} ExeFS: Encrypting: ExHeader"); g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(Constants.CXTExtendedDataHeaderLength))); g.Flush(); @@ -410,7 +327,7 @@ namespace ThreeDS uint datalenB = ((fileHeader.FileSize) % (1024 * 1024)); uint ctroffset = ((fileHeader.FileOffset + header.SectorSize) / 0x10); - byte[] exefsIVWithOffsetForHeader = AddToByteArray(exefsIV, (int)ctroffset); + byte[] exefsIVWithOffsetForHeader = AddToByteArray(partitionHeader.ExeFSIV, (int)ctroffset); var exefsctrmode = CipherUtilities.GetCipher("AES/CTR"); exefsctrmode.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), exefsIVWithOffsetForHeader)); @@ -427,7 +344,7 @@ namespace ThreeDS { g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes(1024 * 1024)))); g.Flush(); - Console.Write("\rPartition {0} ExeFS: Encrypting: {1}... {2} / {3} mb...", p, fileHeader.ReadableFileName, i, datalenM + 1); + Console.Write($"\rPartition {p} ExeFS: Encrypting: {fileHeader.ReadableFileName}... {i} / {datalenM + 1} mb..."); } } @@ -437,7 +354,7 @@ namespace ThreeDS g.Flush(); } - Console.Write("\rPartition {0} ExeFS: Encrypting: {1}... {2} / {3} mb... Done!\r\n", p, fileHeader.ReadableFileName, datalenM + 1, datalenM + 1); + Console.Write($"\rPartition {p} ExeFS: Encrypting: {fileHeader.ReadableFileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n"); } } } @@ -447,19 +364,19 @@ namespace ThreeDS g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin); var exefsctrmode2C_2 = CipherUtilities.GetCipher("AES/CTR"); - exefsctrmode2C_2.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIV)); + exefsctrmode2C_2.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), partitionHeader.ExeFSIV)); g.Write(exefsctrmode2C_2.ProcessBytes(f.ReadBytes((int)header.SectorSize))); g.Flush(); - Console.WriteLine("Partition {0} ExeFS: Encrypting: ExeFS Filename Table", p); + Console.WriteLine($"Partition {p} ExeFS: Encrypting: ExeFS Filename Table"); // encrypt exefs int exefsSizeM = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.SectorSize) / (1024 * 1024); int exefsSizeB = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.SectorSize) % (1024 * 1024); int ctroffsetE = (int)(header.SectorSize / 0x10); - byte[] exefsIVWithOffset = AddToByteArray(exefsIV, ctroffsetE); + byte[] exefsIVWithOffset = AddToByteArray(partitionHeader.ExeFSIV, ctroffsetE); exefsctrmode2C_2 = CipherUtilities.GetCipher("AES/CTR"); exefsctrmode2C_2.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffset)); @@ -472,7 +389,7 @@ namespace ThreeDS { g.Write(exefsctrmode2C_2.ProcessBytes(f.ReadBytes(1024 * 1024))); g.Flush(); - Console.Write("\rPartition {0} ExeFS: Encrypting: {1} / {2} mb", p, i, exefsSizeM + 1); + Console.Write($"\rPartition {p} ExeFS: Encrypting: {i} / {exefsSizeM + 1} mb"); } } if (exefsSizeB > 0) @@ -481,11 +398,11 @@ namespace ThreeDS g.Flush(); } - Console.Write("\rPartition {0} ExeFS: Encrypting: {1} / {2} mb... Done!\r\n", p, exefsSizeM + 1, exefsSizeM + 1); + Console.Write($"\rPartition {p} ExeFS: Encrypting: {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n"); } else { - Console.WriteLine("Partition {0} ExeFS: No Data... Skipping...", p); + Console.WriteLine($"Partition {p} ExeFS: No Data... Skipping..."); } if (partitionHeader.RomFSOffsetInMediaUnits != 0) @@ -509,7 +426,7 @@ namespace ThreeDS } var romfsctrmode = CipherUtilities.GetCipher("AES/CTR"); - romfsctrmode.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), romfsIV)); + romfsctrmode.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), partitionHeader.RomFSIV)); f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin); g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin); @@ -519,7 +436,7 @@ namespace ThreeDS { g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(romfsBlockSize * 1024 * 1024))); g.Flush(); - Console.Write("\rPartition {0} RomFS: Encrypting: {1} / {2} mb", p, i * romfsBlockSize, romfsSizeTotalMb); + Console.Write($"\rPartition {p} RomFS: Encrypting: {i * romfsBlockSize} / {romfsSizeTotalMb} mb"); } } if (romfsSizeB > 0) @@ -528,11 +445,11 @@ namespace ThreeDS g.Flush(); } - Console.Write("\rPartition {0} RomFS: Encrypting: {1} / {2} mb... Done!\r\n", p, romfsSizeTotalMb, romfsSizeTotalMb); + Console.Write($"\rPartition {p} RomFS: Encrypting: {romfsSizeTotalMb} / {romfsSizeTotalMb} mb... Done!\r\n"); } else { - Console.WriteLine("Partition {0} RomFS: No Data... Skipping...", p); + Console.WriteLine($"Partition {p} RomFS: No Data... Skipping..."); } // Write the new CryptoMethod @@ -562,24 +479,85 @@ namespace ThreeDS } } - private static BigInteger RotateLeft(BigInteger val, int r_bits, int max_bits) + /// + /// Perform a rotate left on a BigInteger + /// + /// BigInteger value to rotate + /// Number of bits to rotate + /// Maximum number of bits to rotate on + /// Rotated BigInteger value + private BigInteger RotateLeft(BigInteger val, int r_bits, int max_bits) { return (val << r_bits % max_bits) & (BigInteger.Pow(2, max_bits) - 1) | ((val & (BigInteger.Pow(2, max_bits) - 1)) >> (max_bits - (r_bits % max_bits))); } - private static string ToBytes(int num) + /// + /// Determine the set of keys to be used for encryption or decryption + /// + /// RSA-2048 signature from a partition header + /// BitMasks value for a partition header or backup header + /// CryptoMethod used for the partition + /// Partition number, only used for logging + /// 3DS KeyX value to use + /// 3DS KeyX2C value to use + /// 3DS KeyY value to use + /// 3DS NormalKey value to use + /// 3DS NormalKey2C value to use + private void GetEncryptionKeys(byte[] rsaSignature, BitMasks masks, CryptoMethod method, int partitionNumber, + out BigInteger KeyX, out BigInteger KeyX2C, out BigInteger KeyY, out BigInteger NormalKey, out BigInteger NormalKey2C) { - string numstr = string.Empty; - while (numstr.Length < 16) - { - numstr += (char)(num & 0xFF); - num >>= 8; - } + KeyX = 0; + KeyX2C = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C); + KeyY = new BigInteger(rsaSignature.Take(16).Reverse().ToArray()); // KeyY is the first 16 bytes of the partition RSA-2048 SHA-256 signature - return numstr; + NormalKey = 0; + NormalKey2C = RotateLeft((RotateLeft(KeyX2C, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128); + + if ((masks & BitMasks.FixedCryptoKey) != 0) + { + NormalKey = 0x00; + NormalKey2C = 0x00; + if (partitionNumber == 0) + Console.WriteLine("Encryption Method: Zero Key"); + } + else + { + if (method == CryptoMethod.Original) + { + KeyX = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C); + if (partitionNumber == 0) + Console.WriteLine("Encryption Method: Key 0x2C"); + } + else if (method == CryptoMethod.Seven) + { + KeyX = (development ? Constants.KeyX0x25 : Constants.KeyX0x25); + if (partitionNumber == 0) + Console.WriteLine("Encryption Method: Key 0x25"); + } + else if (method == CryptoMethod.NineThree) + { + KeyX = (development ? Constants.DevKeyX0x18 : Constants.KeyX0x18); + if (partitionNumber == 0) + Console.WriteLine("Encryption Method: Key 0x18"); + } + else if (method == CryptoMethod.NineSix) + { + KeyX = (development ? Constants.DevKeyX0x1B : Constants.KeyX0x1B); + if (partitionNumber == 0) + Console.WriteLine("Encryption Method: Key 0x1B"); + } + + NormalKey = RotateLeft((RotateLeft(KeyX, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128); + } } - private static byte[] AddToByteArray(byte[] input, int add) + /// + /// Add an integer value to a number represented by a byte array + /// + /// Byte array to add to + /// Amount to add + /// Byte array representing the new value + private byte[] AddToByteArray(byte[] input, int add) { int len = input.Length; var bigint = new BigInteger(input.Reverse().ToArray()); @@ -599,7 +577,12 @@ namespace ThreeDS return arr; } - private static byte[] TakeSixteen(BigInteger input) + /// + /// Get a 16-byte array representation of a BigInteger + /// + /// BigInteger value to convert + /// 16-byte array representing the BigInteger + private byte[] TakeSixteen(BigInteger input) { var arr = input.ToByteArray().Take(16).Reverse().ToArray();