Files
NDecrypt/3DSDecrypt/ThreeDSTool.cs
Matt Nadareski 0800d93d22 Notes and minor
2019-04-07 01:32:04 -07:00

622 lines
33 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Numerics;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using ThreeDS.Data;
using ThreeDS.Headers;
namespace ThreeDS
{
public class ThreeDSTool
{
private readonly string filename;
private readonly bool development;
public ThreeDSTool(string filename, bool development)
{
this.filename = filename;
this.development = development;
}
public void Decrypt()
{
if (!File.Exists(filename))
return;
Console.WriteLine(filename);
using (BinaryReader f = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
using (BinaryWriter g = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
{
NCSDHeader header = NCSDHeader.Read(f);
if (header == null)
{
Console.WriteLine("Error: Not a 3DS Rom!");
return;
}
// 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);
continue;
}
// Seek to the beginning of the NCCH partition
f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize), SeekOrigin.Begin);
NCCHHeader partitionHeader = NCCHHeader.Read(f);
if (partitionHeader == null)
{
Console.WriteLine("Partition {0} Unable to read NCCH header", p);
continue;
}
// Check if the 'NoCrypto' bit is set
if ((partitionHeader.Flags.BitMasks & BitMasks.NoCrypto) != 0)
{
Console.WriteLine("Partition {0:d}: Already Decrypted?...", p);
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);
}
// Decrypted extended header, if it exists
if (partitionHeader.ExtendedHeaderSizeInBytes > 0)
{
// Seek to the partition start and skip first part of the header
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"); // ("AES/CTR/NoPadding")
exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), plainIV));
Console.WriteLine("Partition {0} ExeFS: Decrypting: ExHeader", p);
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(Constants.CXTExtendedDataHeaderLength)));
g.Flush();
}
// Decrypt the ExeFS, if it exists
if (partitionHeader.ExeFSSizeInBytes > 0)
{
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
var exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIV));
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes((int)header.SectorSize)));
g.Flush();
Console.WriteLine("Partition {0} ExeFS: Decrypting: ExeFS Filename Table", p);
if (partitionHeader.Flags.CryptoMethod != CryptoMethod.Original)
{
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
ExeFSHeader exefsHeader = ExeFSHeader.Read(f);
if (exefsHeader != null)
{
foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders)
{
if (!fileHeader.IsCodeBinary)
continue;
uint datalenM = ((fileHeader.FileSize) / (1024 * 1024));
uint datalenB = ((fileHeader.FileSize) % (1024 * 1024));
uint ctroffset = ((fileHeader.FileOffset + header.SectorSize) / 0x10);
byte[] exefsIVWithOffsetForHeader = AddToByteArray(exefsIV, (int)ctroffset);
var exefsctrmode = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
exefsctrmode.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), exefsIVWithOffsetForHeader));
exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffsetForHeader));
f.BaseStream.Seek((((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
g.BaseStream.Seek((((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
if (datalenM > 0)
{
for (int i = 0; i < datalenM; i++)
{
g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes(1024 * 1024))));
g.Flush();
Console.Write("\rPartition {0} ExeFS: Decrypting: {1}... {2} / {3} mb...", p, fileHeader.FileName, i, datalenM + 1);
}
}
if (datalenB > 0)
{
g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes((int)datalenB))));
g.Flush();
}
Console.Write("\rPartition {0} ExeFS: Decrypting: {1}... {2} / {3} mb... Done!\r\n", p, fileHeader.FileName, datalenM + 1, datalenM + 1);
}
}
}
// decrypt 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);
exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffset));
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
if (exefsSizeM > 0)
{
for (int i = 0; i < exefsSizeM; i++)
{
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(1024 * 1024)));
g.Flush();
Console.Write("\rPartition {0} ExeFS: Decrypting: {1} / {2} mb", p, i, exefsSizeM + 1);
}
}
if (exefsSizeB > 0)
{
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(exefsSizeB)));
g.Flush();
}
Console.Write("\rPartition {0} ExeFS: Decrypting: {1} / {2} mb... Done!\r\n", p, exefsSizeM + 1, exefsSizeM + 1);
}
else
{
Console.WriteLine("Partition {0} ExeFS: No Data... Skipping...", p);
}
if (partitionHeader.RomFSOffsetInMediaUnits != 0)
{
int romfsSizeM = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) / (1024 * 1024);
int romfsSizeB = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) % (1024 * 1024);
var romfsctrmode = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
romfsctrmode.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), 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);
if (romfsSizeM > 0)
{
for (int i = 0; i < romfsSizeM; i++)
{
g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(1024 * 1024)));
g.Flush();
Console.Write("\rPartition {0} RomFS: Decrypting: {1} / {2} mb", p, i, romfsSizeM + 1);
}
}
if (romfsSizeB > 0)
{
g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(romfsSizeB)));
g.Flush();
}
Console.Write("\rPartition {0} RomFS: Decrypting: {1} / {2} mb... Done!\r\n", p, romfsSizeM + 1, romfsSizeM + 1);
}
else
{
Console.WriteLine("Partition {0} RomFS: No Data... Skipping...", p);
}
// Write the new CryptoMethod
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18B, SeekOrigin.Begin);
g.Write((byte)CryptoMethod.Original);
g.Flush();
// Write the new BitMasks flag
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18F, SeekOrigin.Begin);
BitMasks flag = partitionHeader.Flags.BitMasks;
flag = flag & (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF);
flag = (flag | BitMasks.NoCrypto);
g.Write((byte)flag);
g.Flush();
}
Console.WriteLine("Press Enter to Exit...");
Console.Read();
}
}
/// <summary>
/// Writes wrong header values
/// </summary>
public void Encrypt()
{
if (!File.Exists(filename))
return;
Console.WriteLine(filename);
using (BinaryReader f = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
using (BinaryWriter g = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
{
NCSDHeader header = NCSDHeader.Read(f);
if (header == null)
{
Console.WriteLine("Error: Not a 3DS Rom!");
return;
}
// 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);
continue;
}
// Seek to the beginning of the NCCH partition
f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize), SeekOrigin.Begin);
NCCHHeader partitionHeader = NCCHHeader.Read(f);
if (partitionHeader == null)
{
Console.WriteLine("Partition {0} Unable to read NCCH header", p);
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);
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);
}
// Encrypt extended header, if it exists
if (partitionHeader.ExtendedHeaderSizeInBytes > 0)
{
// Seek to the partition start and skip first part of the header
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"); // ("AES/CTR/NoPadding")
exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), plainIV));
Console.WriteLine("Partition {0} ExeFS: Encrypting: ExHeader", p);
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(Constants.CXTExtendedDataHeaderLength)));
g.Flush();
}
// Encrypt the ExeFS, if it exists
if (partitionHeader.ExeFSSizeInBytes > 0)
{
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
var exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIV));
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes((int)header.SectorSize)));
g.Flush();
Console.WriteLine("Partition {0} ExeFS: Encrypting: ExeFS Filename Table", p);
if (backupFlags.CryptoMethod != CryptoMethod.Original)
{
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
ExeFSHeader exefsHeader = ExeFSHeader.Read(f);
if (exefsHeader != null)
{
foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders)
{
if (!fileHeader.IsCodeBinary)
continue;
uint datalenM = ((fileHeader.FileSize) / (1024 * 1024));
uint datalenB = ((fileHeader.FileSize) % (1024 * 1024));
uint ctroffset = ((fileHeader.FileOffset + header.SectorSize) / 0x10);
byte[] exefsIVWithOffsetForHeader = AddToByteArray(exefsIV, (int)ctroffset);
var exefsctrmode = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
exefsctrmode.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), exefsIVWithOffsetForHeader));
exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffsetForHeader));
f.BaseStream.Seek((((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
g.BaseStream.Seek((((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
if (datalenM > 0)
{
for (int i = 0; i < datalenM; i++)
{
g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes(1024 * 1024))));
g.Flush();
Console.Write("\rPartition {0} ExeFS: Encrypting: {1}... {2} / {3} mb...", p, fileHeader.FileName, i, datalenM + 1);
}
}
if (datalenB > 0)
{
g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes((int)datalenB))));
g.Flush();
}
Console.Write("\rPartition {0} ExeFS: Encrypting: {1}... {2} / {3} mb... Done!\r\n", p, fileHeader.FileName, datalenM + 1, datalenM + 1);
}
}
}
// decrypt 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);
exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffset));
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
if (exefsSizeM > 0)
{
for (int i = 0; i < exefsSizeM; i++)
{
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(1024 * 1024)));
g.Flush();
Console.Write("\rPartition {0} ExeFS: Encrypting: {1} / {2} mb", p, i, exefsSizeM + 1);
}
}
if (exefsSizeB > 0)
{
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(exefsSizeB)));
g.Flush();
}
Console.Write("\rPartition {0} ExeFS: Encrypting: {1} / {2} mb... Done!\r\n", p, exefsSizeM + 1, exefsSizeM + 1);
}
else
{
Console.WriteLine("Partition {0} ExeFS: No Data... Skipping...", p);
}
if (partitionHeader.RomFSOffsetInMediaUnits != 0)
{
int romfsBlockSize = 16; // block size in mb
int romfsSizeM = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) / (romfsBlockSize * (1024 * 1024));
int romfsSizeB = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) % (romfsBlockSize * (1024 * 1024));
int romfsSizeTotalMb = (int)((partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) / (1024 * 1024) + 1);
if (p > 0) // RomFS for partitions 1 and up always use Key0x2C
{
if ((backupFlags.BitMasks & BitMasks.FixedCryptoKey) != 0) // except if using zero-key
{
NormalKey = 0x00;
}
else
{
KeyX = KeyX = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C);
NormalKey = RotateLeft((RotateLeft(KeyX, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128);
}
}
var romfsctrmode = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
romfsctrmode.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), 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);
if (romfsSizeM > 0)
{
for (int i = 0; i < romfsSizeM; i++)
{
g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(romfsBlockSize * 1024 * 1024)));
g.Flush();
Console.Write("\rPartition {0} RomFS: Encrypting: {1} / {2} mb", p, i * romfsBlockSize, romfsSizeTotalMb);
}
}
if (romfsSizeB > 0)
{
g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(romfsSizeB)));
g.Flush();
}
Console.Write("\rPartition {0} RomFS: Encrypting: {1} / {2} mb... Done!\r\n", p, romfsSizeTotalMb, romfsSizeTotalMb);
}
else
{
Console.WriteLine("Partition {0} RomFS: No Data... Skipping...", p);
}
// Write the new CryptoMethod
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18B, SeekOrigin.Begin);
if (p > 0)
{
g.Write((byte)CryptoMethod.Original); // For partitions 1 and up, set crypto-method to 0x00
g.Flush();
}
else
{
g.Write((byte)backupFlags.CryptoMethod); // If partition 0, restore crypto-method from backup flags
g.Flush();
}
// Write the new BitMasks flag
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18F, SeekOrigin.Begin);
BitMasks flag = partitionHeader.Flags.BitMasks;
flag = (flag & ((BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF));
flag = (flag | (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & backupFlags.BitMasks);
g.Write((byte)flag);
g.Flush();
}
Console.WriteLine("Press Enter to Exit...");
Console.Read();
}
}
private static 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)
{
string numstr = string.Empty;
while (numstr.Length < 16)
{
numstr += (char)(num & 0xFF);
num >>= 8;
}
return numstr;
}
private static byte[] AddToByteArray(byte[] input, int add)
{
int len = input.Length;
var bigint = new BigInteger(input.Reverse().ToArray());
bigint += add;
var arr = bigint.ToByteArray().Reverse().ToArray();
if (arr.Length < len)
{
byte[] temp = new byte[len];
for (int i = 0; i < (len - arr.Length); i++)
temp[i] = 0x00;
Array.Copy(arr, 0, temp, len - arr.Length, arr.Length);
arr = temp;
}
return arr;
}
private static byte[] TakeSixteen(BigInteger input)
{
var arr = input.ToByteArray().Take(16).Reverse().ToArray();
if (arr.Length < 16)
{
byte[] temp = new byte[16];
for (int i = 0; i < (16 - arr.Length); i++)
temp[i] = 0x00;
Array.Copy(arr, 0, temp, 16 - arr.Length, arr.Length);
arr = temp;
}
return arr;
}
}
}