Files
NDecrypt/NDecrypt.Core/PartitionKeys.cs

102 lines
3.8 KiB
C#
Raw Normal View History

2024-10-13 11:40:29 -04:00
using System;
2025-09-29 07:47:32 -04:00
using SabreTools.Data.Models.N3DS;
2025-10-07 12:56:23 -04:00
using SabreTools.IO.Extensions;
2024-10-13 11:40:29 -04:00
2024-10-14 00:22:44 -04:00
namespace NDecrypt.Core
2024-10-13 11:40:29 -04:00
{
/// <summary>
/// Set of all keys associated with a partition
/// </summary>
2026-03-22 00:34:36 -04:00
internal class PartitionKeys
2024-10-13 11:40:29 -04:00
{
/// <summary>
/// Primary AES-CTR encryption key
/// </summary>
/// <remarks>Used for both EXE-FS and ROM-FS</remarks>
2024-11-14 01:56:06 -05:00
public byte[] NormalKey { get; private set; }
2024-10-13 11:40:29 -04:00
/// <summary>
/// Secondary AES-CTR encryption key
/// </summary>
/// <remarks>Used for only EXE-FS</remarks>
2024-11-14 01:56:06 -05:00
public byte[] NormalKey2C { get; }
2024-10-13 23:31:13 -04:00
/// <summary>
/// First 16 bytes of the RSA-2048 signature
/// </summary>
/// <remarks>Used as an XOR value during key generation</remarks>
2026-03-22 00:25:19 -04:00
private readonly byte[] KeyY;
2024-10-13 11:40:29 -04:00
/// <summary>
/// Create a new set of keys for a given partition
/// </summary>
/// <param name="signature">RSA-2048 signature from the partition</param>
/// <param name="masks">BitMasks from the partition or backup header</param>
2026-03-22 00:25:19 -04:00
/// <param name="hardwareConstant">AES hardware constant to use</param>
/// <param name="keyX">KeyX value to assign based on crypto method and development status</param>
/// <param name="keyX0x2C">KeyX2C value to assign based on development status</param>
2026-03-22 00:25:19 -04:00
public PartitionKeys(byte[]? signature, BitMasks masks, byte[] hardwareConstant, byte[] keyX, byte[] keyX0x2C)
2024-10-13 11:40:29 -04:00
{
2024-10-13 23:42:00 -04:00
// Validate inputs
2026-01-25 17:20:06 -05:00
if (signature is not null && signature.Length < 16)
2025-11-24 10:07:12 -05:00
throw new ArgumentOutOfRangeException(nameof(signature), $"{nameof(signature)} must be at least 16 bytes");
2024-10-13 23:42:00 -04:00
2024-10-13 11:40:29 -04:00
// Backup headers can't have a KeyY value set
2024-11-14 01:48:14 -05:00
KeyY = new byte[16];
2026-01-25 17:20:06 -05:00
if (signature is not null)
2024-11-14 01:48:14 -05:00
Array.Copy(signature, KeyY, 16);
2024-10-13 11:40:29 -04:00
2024-10-13 23:42:00 -04:00
// Special case for zero-key
2024-11-18 16:20:09 -05:00
#if NET20 || NET35
if ((masks & BitMasks.FixedCryptoKey) > 0)
#else
2024-10-13 11:40:29 -04:00
if (masks.HasFlag(BitMasks.FixedCryptoKey))
2024-11-18 16:20:09 -05:00
#endif
2024-10-13 11:40:29 -04:00
{
2024-10-13 23:42:00 -04:00
Console.WriteLine("Encryption Method: Zero Key");
2024-11-13 21:11:26 -05:00
NormalKey = new byte[16];
NormalKey2C = new byte[16];
2024-10-13 23:42:00 -04:00
return;
2024-10-13 11:40:29 -04:00
}
2024-10-13 23:42:00 -04:00
2026-03-22 00:25:19 -04:00
// Set the standard normal key values
2026-03-22 00:48:53 -04:00
NormalKey = ProcessKey(keyX, KeyY, hardwareConstant);
NormalKey2C = ProcessKey(keyX0x2C, KeyY, hardwareConstant);
2024-10-13 11:40:29 -04:00
}
2024-10-13 23:31:13 -04:00
/// <summary>
/// Set RomFS values based on the bit masks
/// </summary>
/// <param name="masks">BitMasks from the partition or backup header</param>
/// <param name="hardwareConstant">AES hardware constant to use</param>
/// <param name="keyX0x2C">KeyX2C value to assign based on development status</param>
2026-03-22 00:25:19 -04:00
public void SetRomFSValues(BitMasks masks, byte[] hardwareConstant, byte[] keyX0x2C)
2024-10-13 23:31:13 -04:00
{
// NormalKey has a constant value for zero-key
2024-11-18 16:20:09 -05:00
#if NET20 || NET35
if ((masks & BitMasks.FixedCryptoKey) > 0)
#else
2024-10-13 23:31:13 -04:00
if (masks.HasFlag(BitMasks.FixedCryptoKey))
2024-11-18 16:20:09 -05:00
#endif
2024-10-13 23:31:13 -04:00
{
2024-11-13 21:11:26 -05:00
NormalKey = new byte[16];
2024-10-13 23:31:13 -04:00
return;
}
// Encrypting RomFS for partitions 1 and up always use Key0x2C
2026-03-22 00:48:53 -04:00
NormalKey = ProcessKey(keyX0x2C, KeyY, hardwareConstant);
}
/// <summary>
/// Process a key with the standard processing steps
/// </summary>
private static byte[] ProcessKey(byte[] keyBase, byte[] keyY, byte[] hardwareConstant)
{
byte[] processed = keyBase.RotateLeft(2);
processed = processed.Xor(keyY);
processed = processed.Add(hardwareConstant);
return processed.RotateLeft(87);
2024-10-13 23:31:13 -04:00
}
2024-10-13 11:40:29 -04:00
}
2025-11-24 10:07:12 -05:00
}