diff --git a/NDecrypt.Core/PartitionKeys.cs b/NDecrypt.Core/PartitionKeys.cs index 75f300a..65d7c66 100644 --- a/NDecrypt.Core/PartitionKeys.cs +++ b/NDecrypt.Core/PartitionKeys.cs @@ -9,61 +9,31 @@ namespace NDecrypt.Core /// public class PartitionKeys { - public byte[] KeyX { get; private set; } - - public byte[] KeyX2C { get; } - - public byte[] KeyY { get; } - public byte[] NormalKey { get; private set; } public byte[] NormalKey2C { get; } - /// - /// Decryption args to use while processing - /// - private readonly ThreeDSDecryptArgs _decryptArgs; - - /// - /// Indicates if development images are expected - /// - private readonly bool _development; + private readonly byte[] KeyY; /// /// Create a new set of keys for a given partition /// - /// Decryption args representing available keys /// RSA-2048 signature from the partition /// BitMasks from the partition or backup header - /// CryptoMethod from the partition or backup header - /// Determine if development keys are used - public PartitionKeys(ThreeDSDecryptArgs args, byte[]? signature, BitMasks masks, CryptoMethod method, bool development) + /// AES hardware constant to use + /// KeyX value to assign based on crypto method and development status + /// KeyX value to assign based on development status + public PartitionKeys(byte[]? signature, BitMasks masks, byte[] hardwareConstant, byte[] keyX, byte[] keyX0x2C) { // Validate inputs if (signature is not null && signature.Length < 16) throw new ArgumentOutOfRangeException(nameof(signature), $"{nameof(signature)} must be at least 16 bytes"); - // Set fields for future use - _decryptArgs = args; - _development = development; - - // Set the standard KeyX values - KeyX = new byte[16]; - KeyX2C = development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C; - // Backup headers can't have a KeyY value set KeyY = new byte[16]; if (signature is not null) Array.Copy(signature, KeyY, 16); - // Set the standard normal key values - NormalKey = new byte[16]; - - NormalKey2C = KeyX2C.RotateLeft(2); - NormalKey2C = NormalKey2C.Xor(KeyY); - NormalKey2C = NormalKey2C.Add(add: _decryptArgs.AESHardwareConstant); - NormalKey2C = NormalKey2C.RotateLeft(87); - // Special case for zero-key #if NET20 || NET35 if ((masks & BitMasks.FixedCryptoKey) > 0) @@ -77,46 +47,22 @@ namespace NDecrypt.Core return; } - // Set KeyX values based on crypto method - switch (method) - { - case CryptoMethod.Original: - Console.WriteLine("Encryption Method: Key 0x2C"); - KeyX = development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C; - break; - - case CryptoMethod.Seven: - Console.WriteLine("Encryption Method: Key 0x25"); - KeyX = development ? _decryptArgs.DevKeyX0x25 : _decryptArgs.KeyX0x25; - break; - - case CryptoMethod.NineThree: - Console.WriteLine("Encryption Method: Key 0x18"); - KeyX = development ? _decryptArgs.DevKeyX0x18 : _decryptArgs.KeyX0x18; - break; - - case CryptoMethod.NineSix: - Console.WriteLine("Encryption Method: Key 0x1B"); - KeyX = development ? _decryptArgs.DevKeyX0x1B : _decryptArgs.KeyX0x1B; - break; - - // This should never happen - default: - Console.WriteLine("Encryption Method: UNSUPPORTED"); - break; - } - - // Set the normal key based on the new KeyX value - NormalKey = KeyX.RotateLeft(2); + // Set the standard normal key values + NormalKey = keyX.RotateLeft(2); NormalKey = NormalKey.Xor(KeyY); - NormalKey = NormalKey.Add(_decryptArgs.AESHardwareConstant); + NormalKey = NormalKey.Add(hardwareConstant); NormalKey = NormalKey.RotateLeft(87); + + NormalKey2C = keyX0x2C.RotateLeft(2); + NormalKey2C = NormalKey2C.Xor(KeyY); + NormalKey2C = NormalKey2C.Add(hardwareConstant); + NormalKey2C = NormalKey2C.RotateLeft(87); } /// /// Set RomFS values based on the bit masks /// - public void SetRomFSValues(BitMasks masks) + public void SetRomFSValues(BitMasks masks, byte[] hardwareConstant, byte[] keyX0x2C) { // NormalKey has a constant value for zero-key #if NET20 || NET35 @@ -130,11 +76,9 @@ namespace NDecrypt.Core } // Encrypting RomFS for partitions 1 and up always use Key0x2C - KeyX = _development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C; - - NormalKey = KeyX.RotateLeft(2); + NormalKey = keyX0x2C.RotateLeft(2); NormalKey = NormalKey.Xor(KeyY); - NormalKey = NormalKey.Add(_decryptArgs.AESHardwareConstant); + NormalKey = NormalKey.Add(hardwareConstant); NormalKey = NormalKey.RotateLeft(87); } } diff --git a/NDecrypt.Core/ThreeDSTool.cs b/NDecrypt.Core/ThreeDSTool.cs index f2d2cfa..445c94c 100644 --- a/NDecrypt.Core/ThreeDSTool.cs +++ b/NDecrypt.Core/ThreeDSTool.cs @@ -32,6 +32,40 @@ namespace NDecrypt.Core _decryptArgs = decryptArgs; } + #region Common + + /// + /// Get KeyX value for a crypto method and development status combination + /// + private byte[] GetKeyXForCryptoMethod(CryptoMethod method) + { + switch (method) + { + case CryptoMethod.Original: + Console.WriteLine("Encryption Method: Key 0x2C"); + return _development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C; + + case CryptoMethod.Seven: + Console.WriteLine("Encryption Method: Key 0x25"); + return _development ? _decryptArgs.DevKeyX0x25 : _decryptArgs.KeyX0x25; + + case CryptoMethod.NineThree: + Console.WriteLine("Encryption Method: Key 0x18"); + return _development ? _decryptArgs.DevKeyX0x18 : _decryptArgs.KeyX0x18; + + case CryptoMethod.NineSix: + Console.WriteLine("Encryption Method: Key 0x1B"); + return _development ? _decryptArgs.DevKeyX0x1B : _decryptArgs.KeyX0x1B; + + // This should never happen + default: + Console.WriteLine("Encryption Method: UNSUPPORTED"); + return []; + } + } + + #endregion + #region Decrypt /// @@ -165,12 +199,15 @@ namespace NDecrypt.Core return; // Get partition-specific values - byte[]? rsaSignature = partition.RSA2048Signature; + byte[]? signature = partition.RSA2048Signature; BitMasks masks = cart.GetBitMasks(index); CryptoMethod method = cart.GetCryptoMethod(index); // Get the partition keys - _keysMap[index] = new PartitionKeys(_decryptArgs, rsaSignature, masks, method, _development); + byte[] hardwareConstant = _decryptArgs.AESHardwareConstant; + byte[] keyX = GetKeyXForCryptoMethod(method); + byte[] keyX0x2C = _development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C; + _keysMap[index] = new PartitionKeys(signature, masks, hardwareConstant, keyX, keyX0x2C); } /// @@ -583,12 +620,15 @@ namespace NDecrypt.Core return; // Get partition-specific values - byte[]? rsaSignature = partition.RSA2048Signature; + byte[]? signature = partition.RSA2048Signature; BitMasks masks = backupHeader.Flags.BitMasks; CryptoMethod method = backupHeader.Flags.CryptoMethod; // Get the partition keys - _keysMap[index] = new PartitionKeys(_decryptArgs, rsaSignature, masks, method, _development); + byte[] hardwareConstant = _decryptArgs.AESHardwareConstant; + byte[] keyX = GetKeyXForCryptoMethod(method); + byte[] keyX0x2C = _development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C; + _keysMap[index] = new PartitionKeys(signature, masks, hardwareConstant, keyX, keyX0x2C); } /// @@ -820,7 +860,9 @@ namespace NDecrypt.Core if (index > 0) { var backupHeader = cart.BackupHeader; - _keysMap[index].SetRomFSValues(backupHeader!.Flags!.BitMasks); + _keysMap[index].SetRomFSValues(backupHeader.Flags.BitMasks, + hardwareConstant: _decryptArgs.AESHardwareConstant, + keyX0x2C: _development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C); } // Create the RomFS AES cipher for this partition