mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-07-09 02:16:55 +00:00
114 lines
4.5 KiB
C#
114 lines
4.5 KiB
C#
using System;
|
|
using SabreTools.Data.Models.NintendoDisc;
|
|
using SabreTools.Hashing;
|
|
|
|
// TODO: Move to IO
|
|
namespace SabreTools.Wrappers
|
|
{
|
|
public partial class NintendoDisc
|
|
{
|
|
/// <summary>
|
|
/// Retail common key
|
|
/// </summary>
|
|
public static byte[] KoreanCommonKey = [];
|
|
|
|
/// <summary>
|
|
/// Korean common key SHA-256 hash
|
|
/// </summary>
|
|
private const string KoreanCommonKeySHA256 = "b9f42ca27a1e178f0f14ebf1a05d486fa8db8d08875336c4e6e8dfae29f2901c";
|
|
|
|
/// <summary>
|
|
/// Retail common key
|
|
/// </summary>
|
|
public static byte[] RetailCommonKey = [];
|
|
|
|
/// <summary>
|
|
/// Retail common key SHA-256 hash
|
|
/// </summary>
|
|
private const string RetailCommonKeySHA256 = "de38aeab4fe0c36d828a47e6fd315100e7ce234d3b00aa25e6ad6f5ff2824af8";
|
|
|
|
/// <summary>
|
|
/// Decrypt a Wii partition title key from the ticket data.
|
|
/// </summary>
|
|
/// <param name="encryptedTitleKey">16-byte encrypted title key from ticket offset 0x1BF</param>
|
|
/// <param name="titleId">8-byte title ID from ticket offset 0x1DC (big-endian)</param>
|
|
/// <param name="commonKeyIndex">Common key index to use for decryption</param>
|
|
/// <returns>Decrypted 16-byte title key, or null if no key is available for the given index</returns>
|
|
public static byte[]? DecryptTitleKey(byte[]? encryptedTitleKey, byte[]? titleId, int commonKeyIndex)
|
|
{
|
|
if (encryptedTitleKey is null || encryptedTitleKey.Length != 16)
|
|
return null;
|
|
if (titleId is null || titleId.Length != 8)
|
|
return null;
|
|
|
|
byte[] commonKey;
|
|
if (commonKeyIndex == 0)
|
|
commonKey = RetailCommonKey;
|
|
else if (commonKeyIndex == 1)
|
|
commonKey = KoreanCommonKey;
|
|
else
|
|
return null;
|
|
|
|
if (commonKey.Length != 16)
|
|
return null;
|
|
|
|
// IV is the 8-byte title ID padded with zeros to 16 bytes
|
|
byte[] iv = new byte[16];
|
|
Array.Copy(titleId, 0, iv, 0, 8);
|
|
|
|
return AesCbc.Decrypt(encryptedTitleKey, commonKey, iv);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decrypt one Wii block of data (0x7C00 bytes) using AES-128-CBC.
|
|
/// </summary>
|
|
/// <param name="encryptedData">0x7C00 bytes of encrypted block data</param>
|
|
/// <param name="titleKey">16-byte partition title key</param>
|
|
/// <param name="iv">16-byte initialization vector (last 16 bytes of the preceding hash block)</param>
|
|
/// <returns>Decrypted 0x7C00-byte block data, or null on error</returns>
|
|
public static byte[]? DecryptBlock(byte[] encryptedData, byte[] titleKey, byte[] iv)
|
|
{
|
|
if (encryptedData is null || encryptedData.Length != Constants.WiiBlockDataSize)
|
|
return null;
|
|
if (titleKey is null || titleKey.Length != 16)
|
|
return null;
|
|
if (iv is null || iv.Length != 16)
|
|
return null;
|
|
|
|
return AesCbc.Decrypt(encryptedData, titleKey, iv);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate the Korean common key based on hash and length
|
|
/// </summary>
|
|
/// <param name="commonKey">Korean common key to validate</param>
|
|
/// <returns>True if the key was valid, false otherwise</returns>
|
|
public static bool ValidateKoreanCommonKey(byte[]? commonKey)
|
|
{
|
|
// Ignore invalid values
|
|
if (commonKey is null || commonKey.Length != 16)
|
|
return false;
|
|
|
|
// Hash the key and compare
|
|
string? actualHash = HashTool.GetByteArrayHash(commonKey, HashType.SHA256);
|
|
return string.Equals(actualHash, KoreanCommonKeySHA256, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate the retail common key based on hash and length
|
|
/// </summary>
|
|
/// <param name="commonKey">Retail common key to validate</param>
|
|
/// <returns>True if the key was valid, false otherwise</returns>
|
|
public static bool ValidateRetailCommonKey(byte[]? commonKey)
|
|
{
|
|
// Ignore invalid values
|
|
if (commonKey is null || commonKey.Length != 16)
|
|
return false;
|
|
|
|
// Hash the key and compare
|
|
string? actualHash = HashTool.GetByteArrayHash(commonKey, HashType.SHA256);
|
|
return string.Equals(actualHash, RetailCommonKeySHA256, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|
|
}
|