mirror of
https://github.com/SabreTools/NDecrypt.git
synced 2026-07-08 18:06:38 +00:00
Configuration should not be a Core class
This commit is contained in:
@@ -1,30 +1,71 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#if NETFRAMEWORK || NETSTANDARD2_0_OR_GREATER
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Extensions;
|
||||
#endif
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace NDecrypt.Core
|
||||
{
|
||||
public class DSTool : ITool
|
||||
{
|
||||
/// <summary>
|
||||
/// Decryption args to use while processing
|
||||
/// </summary>
|
||||
private readonly DecryptArgs _decryptArgs;
|
||||
#region Properties
|
||||
|
||||
public DSTool(DecryptArgs decryptArgs)
|
||||
/// <summary>
|
||||
/// Blowfish Table
|
||||
/// </summary>
|
||||
public byte[] BlowfishTable
|
||||
{
|
||||
_decryptArgs = decryptArgs;
|
||||
}
|
||||
get;
|
||||
set
|
||||
{
|
||||
// Ignore missing encryption data
|
||||
if (value.Length == 0)
|
||||
return;
|
||||
|
||||
// Validate the blowfish table data
|
||||
byte[]? actual = HashTool.GetByteArrayHashArray(value, HashType.SHA512);
|
||||
if (actual is null || !actual.EqualsExactly(ExpectedNitroSha512Hash))
|
||||
return;
|
||||
|
||||
// Assign the validated value
|
||||
field = value;
|
||||
}
|
||||
} = [];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Test Values
|
||||
|
||||
/// <summary>
|
||||
/// Expected hash for NitroEncryptionData
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedNitroSha512Hash =
|
||||
[
|
||||
0x1A, 0xD6, 0x40, 0x21, 0xFC, 0x3D, 0x1A, 0x9A,
|
||||
0x9B, 0xC0, 0x88, 0x8E, 0x2E, 0x68, 0xDE, 0x4E,
|
||||
0x8A, 0x60, 0x6B, 0x86, 0x63, 0x22, 0xD2, 0xC7,
|
||||
0xC6, 0xD7, 0xD6, 0xCE, 0x65, 0xF5, 0xBA, 0xA7,
|
||||
0xEA, 0x69, 0x63, 0x7E, 0xC9, 0xE4, 0x57, 0x7B,
|
||||
0x01, 0xFD, 0xCE, 0xC2, 0x26, 0x3B, 0xD9, 0x0D,
|
||||
0x84, 0x57, 0xC2, 0x00, 0xB8, 0x56, 0x9F, 0xE5,
|
||||
0x56, 0xDA, 0x8D, 0xDE, 0x84, 0xB8, 0x8E, 0xE4,
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Encrypt
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool EncryptFile(string input, string? output, bool force)
|
||||
{
|
||||
// If the blowfish table is not set, do not process
|
||||
if (BlowfishTable.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Error: Nitro encryption data not provided!");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If the output is provided, copy the input file
|
||||
@@ -52,7 +93,7 @@ namespace NDecrypt.Core
|
||||
}
|
||||
|
||||
// Encrypt the secure area
|
||||
nitro.EncryptSecureArea(_decryptArgs.NitroEncryptionData, force);
|
||||
nitro.EncryptSecureArea(BlowfishTable, force);
|
||||
|
||||
// Write the encrypted secure area
|
||||
using var writer = File.Open(output, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
@@ -77,6 +118,13 @@ namespace NDecrypt.Core
|
||||
/// <inheritdoc/>
|
||||
public bool DecryptFile(string input, string? output, bool force)
|
||||
{
|
||||
// If the blowfish table is not set, do not process
|
||||
if (BlowfishTable.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Error: Nitro encryption data not provided!");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If the output is provided, copy the input file
|
||||
@@ -104,7 +152,7 @@ namespace NDecrypt.Core
|
||||
}
|
||||
|
||||
// Decrypt the secure area
|
||||
nitro.DecryptSecureArea(_decryptArgs.NitroEncryptionData, force);
|
||||
nitro.DecryptSecureArea(BlowfishTable, force);
|
||||
|
||||
// Write the decrypted secure area
|
||||
using var writer = File.Open(output, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NDecrypt.Core
|
||||
/// <summary>
|
||||
/// Decryption args to use while processing
|
||||
/// </summary>
|
||||
private readonly DecryptArgs _decryptArgs;
|
||||
private readonly ThreeDSDecryptArgs _decryptArgs;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if development images are expected
|
||||
@@ -37,11 +37,9 @@ namespace NDecrypt.Core
|
||||
/// <param name="masks">BitMasks from the partition or backup header</param>
|
||||
/// <param name="method">CryptoMethod from the partition or backup header</param>
|
||||
/// <param name="development">Determine if development keys are used</param>
|
||||
public PartitionKeys(DecryptArgs args, byte[]? signature, BitMasks masks, CryptoMethod method, bool development)
|
||||
public PartitionKeys(ThreeDSDecryptArgs args, byte[]? signature, BitMasks masks, CryptoMethod method, bool development)
|
||||
{
|
||||
// Validate inputs
|
||||
if (args.IsReady != true)
|
||||
throw new InvalidOperationException($"{nameof(args)} must be initialized before use");
|
||||
if (signature is not null && signature.Length < 16)
|
||||
throw new ArgumentOutOfRangeException(nameof(signature), $"{nameof(signature)} must be at least 16 bytes");
|
||||
|
||||
@@ -51,7 +49,7 @@ namespace NDecrypt.Core
|
||||
|
||||
// Set the standard KeyX values
|
||||
KeyX = new byte[16];
|
||||
KeyX2C = development ? args.DevKeyX0x2C : args.KeyX0x2C;
|
||||
KeyX2C = development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C;
|
||||
|
||||
// Backup headers can't have a KeyY value set
|
||||
KeyY = new byte[16];
|
||||
@@ -63,7 +61,7 @@ namespace NDecrypt.Core
|
||||
|
||||
NormalKey2C = KeyX2C.RotateLeft(2);
|
||||
NormalKey2C = NormalKey2C.Xor(KeyY);
|
||||
NormalKey2C = NormalKey2C.Add(add: args.AESHardwareConstant);
|
||||
NormalKey2C = NormalKey2C.Add(add: _decryptArgs.AESHardwareConstant);
|
||||
NormalKey2C = NormalKey2C.RotateLeft(87);
|
||||
|
||||
// Special case for zero-key
|
||||
@@ -84,22 +82,22 @@ namespace NDecrypt.Core
|
||||
{
|
||||
case CryptoMethod.Original:
|
||||
Console.WriteLine("Encryption Method: Key 0x2C");
|
||||
KeyX = development ? args.DevKeyX0x2C : args.KeyX0x2C;
|
||||
KeyX = development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C;
|
||||
break;
|
||||
|
||||
case CryptoMethod.Seven:
|
||||
Console.WriteLine("Encryption Method: Key 0x25");
|
||||
KeyX = development ? args.DevKeyX0x25 : args.KeyX0x25;
|
||||
KeyX = development ? _decryptArgs.DevKeyX0x25 : _decryptArgs.KeyX0x25;
|
||||
break;
|
||||
|
||||
case CryptoMethod.NineThree:
|
||||
Console.WriteLine("Encryption Method: Key 0x18");
|
||||
KeyX = development ? args.DevKeyX0x18 : args.KeyX0x18;
|
||||
KeyX = development ? _decryptArgs.DevKeyX0x18 : _decryptArgs.KeyX0x18;
|
||||
break;
|
||||
|
||||
case CryptoMethod.NineSix:
|
||||
Console.WriteLine("Encryption Method: Key 0x1B");
|
||||
KeyX = development ? args.DevKeyX0x1B : args.KeyX0x1B;
|
||||
KeyX = development ? _decryptArgs.DevKeyX0x1B : _decryptArgs.KeyX0x1B;
|
||||
break;
|
||||
|
||||
// This should never happen
|
||||
@@ -111,7 +109,7 @@ namespace NDecrypt.Core
|
||||
// Set the normal key based on the new KeyX value
|
||||
NormalKey = KeyX.RotateLeft(2);
|
||||
NormalKey = NormalKey.Xor(KeyY);
|
||||
NormalKey = NormalKey.Add(args.AESHardwareConstant);
|
||||
NormalKey = NormalKey.Add(_decryptArgs.AESHardwareConstant);
|
||||
NormalKey = NormalKey.RotateLeft(87);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,97 +1,205 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Encryption;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace NDecrypt.Core
|
||||
{
|
||||
public class DecryptArgs
|
||||
/// <summary>
|
||||
/// Nintendo 3DS encryption/decryption values
|
||||
/// </summary>
|
||||
public class ThreeDSDecryptArgs
|
||||
{
|
||||
#region Common Fields
|
||||
|
||||
/// <summary>
|
||||
/// Represents if all of the keys have been initialized properly
|
||||
/// </summary>
|
||||
public bool? IsReady { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region DS-Specific Fields
|
||||
|
||||
/// <summary>
|
||||
/// Blowfish Table
|
||||
/// </summary>
|
||||
public byte[] NitroEncryptionData { get; private set; } = [];
|
||||
|
||||
#endregion
|
||||
|
||||
#region 3DS-Specific Fields
|
||||
|
||||
/// <summary>
|
||||
/// AES Hardware Constant
|
||||
/// </summary>
|
||||
public byte[] AESHardwareConstant { get; private set; } = [];
|
||||
/// TODO: Validate this value on assignment
|
||||
public byte[] AESHardwareConstant { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x18 (New 3DS 9.3)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x18 { get; private set; } = [];
|
||||
public byte[] KeyX0x18
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
// Ignore missing key data
|
||||
if (value.Length == 0)
|
||||
return;
|
||||
|
||||
// Validate the key data
|
||||
var cipher = AESCTR.CreateEncryptionCipher(KeyX0x18, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedKeyX0x18))
|
||||
return;
|
||||
|
||||
// Assign the validated value
|
||||
field = value;
|
||||
}
|
||||
} = [];
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x18 (New 3DS 9.3)
|
||||
/// </summary>
|
||||
public byte[] DevKeyX0x18 { get; private set; } = [];
|
||||
public byte[] DevKeyX0x18
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
// Ignore missing key data
|
||||
if (value.Length == 0)
|
||||
return;
|
||||
|
||||
// Validate the key data
|
||||
var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x18, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedDevKeyX0x18))
|
||||
return;
|
||||
|
||||
// Assign the validated value
|
||||
field = value;
|
||||
}
|
||||
} = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x1B (New 3DS 9.6)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x1B { get; private set; } = [];
|
||||
public byte[] KeyX0x1B
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
// Ignore missing key data
|
||||
if (value.Length == 0)
|
||||
return;
|
||||
|
||||
// Validate the key data
|
||||
var cipher = AESCTR.CreateEncryptionCipher(KeyX0x1B, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedKeyX0x1B))
|
||||
return;
|
||||
|
||||
// Assign the validated value
|
||||
field = value;
|
||||
}
|
||||
} = [];
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x1B New 3DS 9.6)
|
||||
/// </summary>
|
||||
public byte[] DevKeyX0x1B { get; private set; } = [];
|
||||
public byte[] DevKeyX0x1B
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
// Ignore missing key data
|
||||
if (value.Length == 0)
|
||||
return;
|
||||
|
||||
// Validate the key data
|
||||
var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x1B, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedDevKeyX0x1B))
|
||||
return;
|
||||
|
||||
// Assign the validated value
|
||||
field = value;
|
||||
}
|
||||
} = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x25 (> 7.x)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x25 { get; private set; } = [];
|
||||
public byte[] KeyX0x25
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
// Ignore missing key data
|
||||
if (value.Length == 0)
|
||||
return;
|
||||
|
||||
// Validate the key data
|
||||
var cipher = AESCTR.CreateEncryptionCipher(KeyX0x25, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedKeyX0x25))
|
||||
return;
|
||||
|
||||
// Assign the validated value
|
||||
field = value;
|
||||
}
|
||||
} = [];
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x25 (> 7.x)
|
||||
/// </summary>
|
||||
public byte[] DevKeyX0x25 { get; private set; } = [];
|
||||
public byte[] DevKeyX0x25
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
// Ignore missing key data
|
||||
if (value.Length == 0)
|
||||
return;
|
||||
|
||||
// Validate the key data
|
||||
var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x25, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedDevKeyX0x25))
|
||||
return;
|
||||
|
||||
// Assign the validated value
|
||||
field = value;
|
||||
}
|
||||
} = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x2C (< 6.x)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x2C { get; private set; } = [];
|
||||
public byte[] KeyX0x2C
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
// Ignore missing key data
|
||||
if (value.Length == 0)
|
||||
return;
|
||||
|
||||
// Validate the key data
|
||||
var cipher = AESCTR.CreateEncryptionCipher(KeyX0x2C, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedKeyX0x2C))
|
||||
return;
|
||||
|
||||
// Assign the validated value
|
||||
field = value;
|
||||
}
|
||||
} = [];
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x2C (< 6.x)
|
||||
/// </summary>
|
||||
public byte[] DevKeyX0x2C { get; private set; } = [];
|
||||
public byte[] DevKeyX0x2C
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
// Ignore missing key data
|
||||
if (value.Length == 0)
|
||||
return;
|
||||
|
||||
#endregion
|
||||
// Validate the key data
|
||||
var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x2C, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedDevKeyX0x2C))
|
||||
return;
|
||||
|
||||
// Assign the validated value
|
||||
field = value;
|
||||
}
|
||||
} = [];
|
||||
|
||||
#region Internal Test Values
|
||||
|
||||
/// <summary>
|
||||
/// Expected hash for NitroEncryptionData
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedNitroSha512Hash =
|
||||
[
|
||||
0x1A, 0xD6, 0x40, 0x21, 0xFC, 0x3D, 0x1A, 0x9A,
|
||||
0x9B, 0xC0, 0x88, 0x8E, 0x2E, 0x68, 0xDE, 0x4E,
|
||||
0x8A, 0x60, 0x6B, 0x86, 0x63, 0x22, 0xD2, 0xC7,
|
||||
0xC6, 0xD7, 0xD6, 0xCE, 0x65, 0xF5, 0xBA, 0xA7,
|
||||
0xEA, 0x69, 0x63, 0x7E, 0xC9, 0xE4, 0x57, 0x7B,
|
||||
0x01, 0xFD, 0xCE, 0xC2, 0x26, 0x3B, 0xD9, 0x0D,
|
||||
0x84, 0x57, 0xC2, 0x00, 0xB8, 0x56, 0x9F, 0xE5,
|
||||
0x56, 0xDA, 0x8D, 0xDE, 0x84, 0xB8, 0x8E, 0xE4,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Initial value for key validation tests
|
||||
/// </summary>
|
||||
@@ -237,155 +345,5 @@ namespace NDecrypt.Core
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Setup all of the necessary constants
|
||||
/// </summary>
|
||||
/// <param name="keyfile">Path to the keyfile</param>
|
||||
public DecryptArgs(string? config)
|
||||
{
|
||||
if (config is null || !File.Exists(config))
|
||||
{
|
||||
IsReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to read the configuration file
|
||||
var configObj = Configuration.Create(config);
|
||||
if (configObj is null)
|
||||
{
|
||||
IsReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the fields from the configuration
|
||||
NitroEncryptionData = configObj.NitroEncryptionData.FromHexString() ?? [];
|
||||
AESHardwareConstant = configObj.AESHardwareConstant.FromHexString() ?? [];
|
||||
KeyX0x18 = configObj.KeyX0x18.FromHexString() ?? [];
|
||||
KeyX0x1B = configObj.KeyX0x1B.FromHexString() ?? [];
|
||||
KeyX0x25 = configObj.KeyX0x25.FromHexString() ?? [];
|
||||
KeyX0x2C = configObj.KeyX0x2C.FromHexString() ?? [];
|
||||
DevKeyX0x18 = configObj.DevKeyX0x18.FromHexString() ?? [];
|
||||
DevKeyX0x1B = configObj.DevKeyX0x1B.FromHexString() ?? [];
|
||||
DevKeyX0x25 = configObj.DevKeyX0x25.FromHexString() ?? [];
|
||||
DevKeyX0x2C = configObj.DevKeyX0x2C.FromHexString() ?? [];
|
||||
|
||||
IsReady = true;
|
||||
ValidateKeys();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that all keys provided are going to be valid
|
||||
/// </summary>
|
||||
/// <remarks>Does not know what the keys are, just the result</remarks>
|
||||
private void ValidateKeys()
|
||||
{
|
||||
// NitroEncryptionData
|
||||
if (NitroEncryptionData.Length > 0)
|
||||
{
|
||||
byte[]? actual = HashTool.GetByteArrayHashArray(NitroEncryptionData, HashType.SHA512);
|
||||
if (actual is null || !actual.EqualsExactly(ExpectedNitroSha512Hash))
|
||||
{
|
||||
Console.WriteLine($"NitroEncryptionData invalid value, disabling...");
|
||||
NitroEncryptionData = [];
|
||||
}
|
||||
}
|
||||
|
||||
// KeyX0x18
|
||||
if (KeyX0x18.Length > 0)
|
||||
{
|
||||
var cipher = AESCTR.CreateEncryptionCipher(KeyX0x18, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedKeyX0x18))
|
||||
{
|
||||
Console.WriteLine($"KeyX0x18 invalid value, disabling...");
|
||||
KeyX0x18 = [];
|
||||
}
|
||||
}
|
||||
|
||||
// DevKeyX0x18
|
||||
if (DevKeyX0x18.Length > 0)
|
||||
{
|
||||
var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x18, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedDevKeyX0x18))
|
||||
{
|
||||
Console.WriteLine($"DevKeyX0x18 invalid value, disabling...");
|
||||
DevKeyX0x18 = [];
|
||||
}
|
||||
}
|
||||
|
||||
// KeyX0x1B
|
||||
if (KeyX0x1B.Length > 0)
|
||||
{
|
||||
var cipher = AESCTR.CreateEncryptionCipher(KeyX0x1B, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedKeyX0x1B))
|
||||
{
|
||||
Console.WriteLine($"KeyX0x1B invalid value, disabling...");
|
||||
KeyX0x1B = [];
|
||||
}
|
||||
}
|
||||
|
||||
// DevKeyX0x1B
|
||||
if (DevKeyX0x1B.Length > 0)
|
||||
{
|
||||
var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x1B, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedDevKeyX0x1B))
|
||||
{
|
||||
Console.WriteLine($"DevKeyX0x1B invalid value, disabling...");
|
||||
DevKeyX0x1B = [];
|
||||
}
|
||||
}
|
||||
|
||||
// KeyX0x25
|
||||
if (KeyX0x25.Length > 0)
|
||||
{
|
||||
var cipher = AESCTR.CreateEncryptionCipher(KeyX0x25, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedKeyX0x25))
|
||||
{
|
||||
Console.WriteLine($"KeyX0x25 invalid value, disabling...");
|
||||
KeyX0x25 = [];
|
||||
}
|
||||
}
|
||||
|
||||
// DevKeyX0x25
|
||||
if (DevKeyX0x25.Length > 0)
|
||||
{
|
||||
var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x25, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedDevKeyX0x25))
|
||||
{
|
||||
Console.WriteLine($"DevKeyX0x25 invalid value, disabling...");
|
||||
DevKeyX0x25 = [];
|
||||
}
|
||||
}
|
||||
|
||||
// KeyX0x2C
|
||||
if (KeyX0x2C.Length > 0)
|
||||
{
|
||||
var cipher = AESCTR.CreateEncryptionCipher(KeyX0x2C, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedKeyX0x2C))
|
||||
{
|
||||
Console.WriteLine($"KeyX0x2C invalid value, disabling...");
|
||||
KeyX0x2C = [];
|
||||
}
|
||||
}
|
||||
|
||||
// DevKeyX0x2C
|
||||
if (DevKeyX0x2C.Length > 0)
|
||||
{
|
||||
var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x2C, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!actual.EqualsExactly(ExpectedDevKeyX0x2C))
|
||||
{
|
||||
Console.WriteLine($"DevKeyX0x2C invalid value, disabling...");
|
||||
DevKeyX0x2C = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace NDecrypt.Core
|
||||
/// <summary>
|
||||
/// Decryption args to use while processing
|
||||
/// </summary>
|
||||
private readonly DecryptArgs _decryptArgs;
|
||||
private readonly ThreeDSDecryptArgs _decryptArgs;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if development images are expected
|
||||
@@ -26,7 +26,7 @@ namespace NDecrypt.Core
|
||||
/// </summary>
|
||||
private readonly PartitionKeys[] _keysMap = new PartitionKeys[8];
|
||||
|
||||
public ThreeDSTool(bool development, DecryptArgs decryptArgs)
|
||||
public ThreeDSTool(bool development, ThreeDSDecryptArgs decryptArgs)
|
||||
{
|
||||
_development = development;
|
||||
_decryptArgs = decryptArgs;
|
||||
@@ -37,13 +37,6 @@ namespace NDecrypt.Core
|
||||
/// <inheritdoc/>
|
||||
public bool DecryptFile(string input, string? output, bool force)
|
||||
{
|
||||
// Ensure the constants are all set
|
||||
if (_decryptArgs.IsReady != true)
|
||||
{
|
||||
Console.WriteLine("Could not read keys. Please make sure the file exists and try again.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If the output is provided, copy the input file
|
||||
@@ -456,13 +449,6 @@ namespace NDecrypt.Core
|
||||
/// <inheritdoc/>
|
||||
public bool EncryptFile(string input, string? output, bool force)
|
||||
{
|
||||
// Ensure the constants are all set
|
||||
if (_decryptArgs.IsReady != true)
|
||||
{
|
||||
Console.WriteLine("Could not read keys. Please make sure the file exists and try again.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If the output is provided, copy the input file
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NDecrypt.Core
|
||||
namespace NDecrypt
|
||||
{
|
||||
internal class Configuration
|
||||
{
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using NDecrypt.Core;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace NDecrypt.Features
|
||||
{
|
||||
@@ -31,7 +32,7 @@ namespace NDecrypt.Features
|
||||
/// <summary>
|
||||
/// Mapping of reusable tools
|
||||
/// </summary>
|
||||
private readonly Dictionary<FileType, ITool> _tools = [];
|
||||
private readonly Dictionary<FileType, ITool?> _tools = [];
|
||||
|
||||
protected BaseFeature(string name, string[] flags, string description, string? detailed = null)
|
||||
: base(name, flags, description, detailed)
|
||||
@@ -80,10 +81,41 @@ namespace NDecrypt.Features
|
||||
/// </summary>
|
||||
private void InitializeTools()
|
||||
{
|
||||
// Set default values for tools
|
||||
_tools[FileType.NDS] = null;
|
||||
_tools[FileType.N3DS] = null;
|
||||
|
||||
var decryptArgs = new DecryptArgs(GetString(ConfigName, "config.json"));
|
||||
_tools[FileType.NDS] = new DSTool(decryptArgs);
|
||||
_tools[FileType.N3DS] = new ThreeDSTool(GetBoolean(DevelopmentName), decryptArgs);
|
||||
// Check the configuration path
|
||||
string? configPath = GetString(ConfigName, "config.json");
|
||||
if (configPath is null || !File.Exists(configPath))
|
||||
return;
|
||||
|
||||
// Try to read the configuration file
|
||||
var config = Configuration.Create(configPath);
|
||||
if (config is null)
|
||||
return;
|
||||
|
||||
// Create the DS tool
|
||||
_tools[FileType.NDS] = new DSTool
|
||||
{
|
||||
BlowfishTable = config.NitroEncryptionData.FromHexString() ?? [],
|
||||
};
|
||||
|
||||
// Create the 3DS tool
|
||||
bool development = GetBoolean(DevelopmentName);
|
||||
var decryptArgs = new ThreeDSDecryptArgs
|
||||
{
|
||||
AESHardwareConstant = config.AESHardwareConstant.FromHexString() ?? [],
|
||||
KeyX0x18 = config.KeyX0x18.FromHexString() ?? [],
|
||||
KeyX0x1B = config.KeyX0x1B.FromHexString() ?? [],
|
||||
KeyX0x25 = config.KeyX0x25.FromHexString() ?? [],
|
||||
KeyX0x2C = config.KeyX0x2C.FromHexString() ?? [],
|
||||
DevKeyX0x18 = config.DevKeyX0x18.FromHexString() ?? [],
|
||||
DevKeyX0x1B = config.DevKeyX0x1B.FromHexString() ?? [],
|
||||
DevKeyX0x25 = config.DevKeyX0x25.FromHexString() ?? [],
|
||||
DevKeyX0x2C = config.DevKeyX0x2C.FromHexString() ?? []
|
||||
};
|
||||
_tools[FileType.N3DS] = new ThreeDSTool(development, decryptArgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user