mirror of
https://github.com/SabreTools/NDecrypt.git
synced 2026-09-21 22:35:03 +00:00
Modularization, cleanup
This commit is contained in:
@@ -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];
|
||||
|
||||
@@ -11,7 +11,14 @@ namespace ThreeDS
|
||||
{
|
||||
public class ThreeDSTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the input 3DS file
|
||||
/// </summary>
|
||||
private readonly string filename;
|
||||
|
||||
/// <summary>
|
||||
/// Flag to detrmine if development keys should be used
|
||||
/// </summary>
|
||||
private readonly bool development;
|
||||
|
||||
public ThreeDSTool(string filename, bool development)
|
||||
@@ -20,6 +27,9 @@ namespace ThreeDS
|
||||
this.development = development;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to decrypt a 3DS file
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to encrypt a 3DS file
|
||||
/// </summary>
|
||||
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)
|
||||
/// <summary>
|
||||
/// Perform a rotate left on a BigInteger
|
||||
/// </summary>
|
||||
/// <param name="val">BigInteger value to rotate</param>
|
||||
/// <param name="r_bits">Number of bits to rotate</param>
|
||||
/// <param name="max_bits">Maximum number of bits to rotate on</param>
|
||||
/// <returns>Rotated BigInteger value</returns>
|
||||
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)
|
||||
/// <summary>
|
||||
/// Determine the set of keys to be used for encryption or decryption
|
||||
/// </summary>
|
||||
/// <param name="rsaSignature">RSA-2048 signature from a partition header</param>
|
||||
/// <param name="masks">BitMasks value for a partition header or backup header</param>
|
||||
/// <param name="method">CryptoMethod used for the partition</param>
|
||||
/// <param name="partitionNumber">Partition number, only used for logging</param>
|
||||
/// <param name="KeyX">3DS KeyX value to use</param>
|
||||
/// <param name="KeyX2C">3DS KeyX2C value to use</param>
|
||||
/// <param name="KeyY">3DS KeyY value to use</param>
|
||||
/// <param name="NormalKey">3DS NormalKey value to use</param>
|
||||
/// <param name="NormalKey2C">3DS NormalKey2C value to use</param>
|
||||
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)
|
||||
/// <summary>
|
||||
/// Add an integer value to a number represented by a byte array
|
||||
/// </summary>
|
||||
/// <param name="input">Byte array to add to</param>
|
||||
/// <param name="add">Amount to add</param>
|
||||
/// <returns>Byte array representing the new value</returns>
|
||||
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)
|
||||
/// <summary>
|
||||
/// Get a 16-byte array representation of a BigInteger
|
||||
/// </summary>
|
||||
/// <param name="input">BigInteger value to convert</param>
|
||||
/// <returns>16-byte array representing the BigInteger</returns>
|
||||
private byte[] TakeSixteen(BigInteger input)
|
||||
{
|
||||
var arr = input.ToByteArray().Take(16).Reverse().ToArray();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user