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();