mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Split ExternalKeyReader
This commit is contained in:
parent
bac541947f
commit
fa79db2285
5 changed files with 457 additions and 425 deletions
|
@ -1,10 +1,17 @@
|
||||||
using System.Runtime.CompilerServices;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys
|
namespace LibHac.Common.Keys
|
||||||
{
|
{
|
||||||
internal static partial class DefaultKeySet
|
internal static partial class DefaultKeySet
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="KeySet"/> that contains any keys that have been embedded in the library.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The created <see cref="KeySet"/>.</returns>
|
||||||
public static KeySet CreateDefaultKeySet()
|
public static KeySet CreateDefaultKeySet()
|
||||||
{
|
{
|
||||||
var keySet = new KeySet();
|
var keySet = new KeySet();
|
||||||
|
@ -67,5 +74,108 @@ namespace LibHac.Common.Keys
|
||||||
|
|
||||||
return keySet;
|
return keySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="List{T}"/> of the <see cref="KeyInfo"/> of all keys that are loadable by default.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The created list.</returns>
|
||||||
|
public static List<KeyInfo> CreateKeyList()
|
||||||
|
{
|
||||||
|
// Update this value if more keys are added
|
||||||
|
var keys = new List<KeyInfo>(70);
|
||||||
|
|
||||||
|
// Keys with a group value of -1 are keys that will be read but not written.
|
||||||
|
// This is for compatibility since some keys had other names in the past.
|
||||||
|
|
||||||
|
// TSEC secrets aren't public yet, so the TSEC root keys will be treated as
|
||||||
|
// root keys even though they're derived.
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(10, Type.DeviceRoot, "secure_boot_key", (set, i) => set.SecureBootKey));
|
||||||
|
keys.Add(new KeyInfo(11, Type.DeviceRoot, "tsec_key", (set, i) => set.TsecKey));
|
||||||
|
keys.Add(new KeyInfo(12, Type.DeviceDrvd, "device_key", (set, i) => set.DeviceKey));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(20, Type.CommonRoot, "tsec_root_kek", (set, i) => set.TsecRootKek));
|
||||||
|
keys.Add(new KeyInfo(21, Type.CommonRoot, "package1_mac_kek", (set, i) => set.Package1MacKek));
|
||||||
|
keys.Add(new KeyInfo(22, Type.CommonRoot, "package1_kek", (set, i) => set.Package1Kek));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(30, Type.CommonRoot, "tsec_auth_signature", 0, 0x20, (set, i) => set.TsecAuthSignatures[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(40, Type.CommonRoot, "tsec_root_key", 0, 0x20, (set, i) => set.TsecRootKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(50, Type.CommonSeed, "keyblob_mac_key_source", (set, i) => set.KeyBlobMacKeySource));
|
||||||
|
keys.Add(new KeyInfo(51, Type.CommonSeed, "keyblob_key_source", 0, 6, (set, i) => set.KeyBlobKeySources[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(55, Type.DeviceDrvd, "keyblob_key", 0, 6, (set, i) => set.KeyBlobKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(60, Type.DeviceDrvd, "keyblob_mac_key", 0, 6, (set, i) => set.KeyBlobMacKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(70, Type.DeviceRoot, "encrypted_keyblob", 0, 6, (set, i) => set.EncryptedKeyBlobs[i].Bytes));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(80, Type.CommonRoot, "keyblob", 0, 6, (set, i) => set.KeyBlobs[i].Bytes));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(90, Type.CommonSeed, "master_kek_source", 6, 0x20, (set, i) => set.MasterKekSources[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(100, Type.CommonRoot, "mariko_bek", (set, i) => set.MarikoBek));
|
||||||
|
keys.Add(new KeyInfo(101, Type.CommonRoot, "mariko_kek", (set, i) => set.MarikoKek));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(110, Type.CommonRoot, "mariko_aes_class_key", 0, 0xC, (set, i) => set.MarikoAesClassKeys[i]));
|
||||||
|
keys.Add(new KeyInfo(120, Type.CommonSeedDiff, "mariko_master_kek_source", 0, 0x20, (set, i) => set.MarikoMasterKekSources[i]));
|
||||||
|
keys.Add(new KeyInfo(130, Type.CommonDrvd, "master_kek", 0, 0x20, (set, i) => set.MasterKeks[i]));
|
||||||
|
keys.Add(new KeyInfo(140, Type.CommonSeed, "master_key_source", (set, i) => set.MasterKeySource));
|
||||||
|
keys.Add(new KeyInfo(150, Type.CommonDrvd, "master_key", 0, 0x20, (set, i) => set.MasterKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(160, Type.CommonDrvd, "package1_key", 0, 0x20, (set, i) => set.Package1Keys[i]));
|
||||||
|
keys.Add(new KeyInfo(170, Type.CommonDrvd, "package1_mac_key", 6, 0x20, (set, i) => set.Package1MacKeys[i]));
|
||||||
|
keys.Add(new KeyInfo(180, Type.CommonSeed, "package2_key_source", (set, i) => set.Package2KeySource));
|
||||||
|
keys.Add(new KeyInfo(190, Type.CommonDrvd, "package2_key", 0, 0x20, (set, i) => set.Package2Keys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(200, Type.CommonSeed, "bis_kek_source", (set, i) => set.BisKekSource));
|
||||||
|
keys.Add(new KeyInfo(201, Type.CommonSeed, "bis_key_source", 0, 4, (set, i) => set.BisKeySources[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(205, Type.DeviceDrvd, "bis_key", 0, 4, (set, i) => set.BisKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(210, Type.CommonSeed, "per_console_key_source", (set, i) => set.PerConsoleKeySource));
|
||||||
|
keys.Add(new KeyInfo(211, Type.CommonSeed, "retail_specific_aes_key_source", (set, i) => set.RetailSpecificAesKeySource));
|
||||||
|
keys.Add(new KeyInfo(212, Type.CommonSeed, "aes_kek_generation_source", (set, i) => set.AesKekGenerationSource));
|
||||||
|
keys.Add(new KeyInfo(213, Type.CommonSeed, "aes_key_generation_source", (set, i) => set.AesKeyGenerationSource));
|
||||||
|
keys.Add(new KeyInfo(214, Type.CommonSeed, "titlekek_source", (set, i) => set.TitleKekSource));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(220, Type.CommonDrvd, "titlekek", 0, 0x20, (set, i) => set.TitleKeks[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(230, Type.CommonSeed, "header_kek_source", (set, i) => set.HeaderKekSource));
|
||||||
|
keys.Add(new KeyInfo(231, Type.CommonSeed, "header_key_source", (set, i) => set.HeaderKeySource));
|
||||||
|
keys.Add(new KeyInfo(232, Type.CommonDrvd, "header_key", (set, i) => set.HeaderKey));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(240, Type.CommonSeed, "key_area_key_application_source", (set, i) => set.KeyAreaKeyApplicationSource));
|
||||||
|
keys.Add(new KeyInfo(241, Type.CommonSeed, "key_area_key_ocean_source", (set, i) => set.KeyAreaKeyOceanSource));
|
||||||
|
keys.Add(new KeyInfo(242, Type.CommonSeed, "key_area_key_system_source", (set, i) => set.KeyAreaKeySystemSource));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(250, Type.CommonSeed, "save_mac_kek_source", (set, i) => set.DeviceUniqueSaveMacKekSource));
|
||||||
|
keys.Add(new KeyInfo(251, Type.CommonSeed, "save_mac_key_source", 0, 2, (set, i) => set.DeviceUniqueSaveMacKeySources[i]));
|
||||||
|
keys.Add(new KeyInfo(252, Type.DeviceDrvd, "save_mac_key", 0, 2, (set, i) => set.DeviceUniqueSaveMacKeys[i]));
|
||||||
|
keys.Add(new KeyInfo(-01, Type.CommonSeed, "save_mac_key_source", (set, i) => set.DeviceUniqueSaveMacKeySources[0]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(253, Type.CommonSeed, "save_mac_sd_card_kek_source", (set, i) => set.SeedUniqueSaveMacKekSource));
|
||||||
|
keys.Add(new KeyInfo(254, Type.CommonSeed, "save_mac_sd_card_key_source", (set, i) => set.SeedUniqueSaveMacKeySource));
|
||||||
|
keys.Add(new KeyInfo(255, Type.DeviceDrvd, "save_mac_sd_card_key", (set, i) => set.SeedUniqueSaveMacKey));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(260, Type.DeviceRoot, "sd_seed", (set, i) => set.SdCardEncryptionSeed));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(261, Type.CommonSeed, "sd_card_kek_source", (set, i) => set.SdCardKekSource));
|
||||||
|
keys.Add(new KeyInfo(262, Type.CommonSeed, "sd_card_save_key_source", (set, i) => set.SdCardKeySources[0]));
|
||||||
|
keys.Add(new KeyInfo(263, Type.CommonSeed, "sd_card_nca_key_source", (set, i) => set.SdCardKeySources[1]));
|
||||||
|
keys.Add(new KeyInfo(264, Type.CommonSeed, "sd_card_custom_storage_key_source", (set, i) => set.SdCardKeySources[2]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(270, Type.CommonSeedDiff, "xci_header_key", (set, i) => set.XciHeaderKey));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(280, Type.CommonRoot, "eticket_rsa_kek", (set, i) => set.ETicketRsaKek));
|
||||||
|
keys.Add(new KeyInfo(281, Type.CommonRoot, "ssl_rsa_kek", (set, i) => set.SslRsaKek));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(290, Type.CommonDrvd, "key_area_key_application", 0, 0x20, (set, i) => set.KeyAreaKeys[i][0]));
|
||||||
|
keys.Add(new KeyInfo(300, Type.CommonDrvd, "key_area_key_ocean", 0, 0x20, (set, i) => set.KeyAreaKeys[i][1]));
|
||||||
|
keys.Add(new KeyInfo(310, Type.CommonDrvd, "key_area_key_system", 0, 0x20, (set, i) => set.KeyAreaKeys[i][2]));
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using LibHac.Diag;
|
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Spl;
|
using LibHac.Spl;
|
||||||
|
|
||||||
|
@ -12,140 +9,6 @@ namespace LibHac.Common.Keys
|
||||||
{
|
{
|
||||||
public static class ExternalKeyReader
|
public static class ExternalKeyReader
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{" + nameof(Name) + "}")]
|
|
||||||
public readonly struct KeyInfo
|
|
||||||
{
|
|
||||||
public readonly string Name;
|
|
||||||
public readonly KeyGetter Getter;
|
|
||||||
public readonly int Group;
|
|
||||||
public readonly KeyRangeType RangeType;
|
|
||||||
public readonly KeyType Type;
|
|
||||||
public readonly byte RangeStart;
|
|
||||||
public readonly byte RangeEnd;
|
|
||||||
|
|
||||||
public int NameLength => Name.Length + (RangeType == KeyRangeType.Range ? 3 : 0);
|
|
||||||
|
|
||||||
public delegate Span<byte> KeyGetter(KeySet keySet, int i);
|
|
||||||
|
|
||||||
public KeyInfo(int group, KeyType type, string name, KeyGetter retrieveFunc)
|
|
||||||
{
|
|
||||||
Assert.AssertTrue(IsKeyTypeValid(type));
|
|
||||||
|
|
||||||
Name = name;
|
|
||||||
RangeType = KeyRangeType.Single;
|
|
||||||
Type = type;
|
|
||||||
RangeStart = default;
|
|
||||||
RangeEnd = default;
|
|
||||||
Group = group;
|
|
||||||
Getter = retrieveFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyInfo(int group, KeyType type, string name, byte rangeStart, byte rangeEnd, KeyGetter retrieveFunc)
|
|
||||||
{
|
|
||||||
Assert.AssertTrue(IsKeyTypeValid(type));
|
|
||||||
|
|
||||||
Name = name;
|
|
||||||
RangeType = KeyRangeType.Range;
|
|
||||||
Type = type;
|
|
||||||
RangeStart = rangeStart;
|
|
||||||
RangeEnd = rangeEnd;
|
|
||||||
Group = group;
|
|
||||||
Getter = retrieveFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Matches(string keyName, out int keyIndex, out bool isDev)
|
|
||||||
{
|
|
||||||
keyIndex = default;
|
|
||||||
isDev = default;
|
|
||||||
|
|
||||||
return RangeType switch
|
|
||||||
{
|
|
||||||
KeyRangeType.Single => MatchesSingle(keyName, out isDev),
|
|
||||||
KeyRangeType.Range => MatchesRangedKey(keyName, ref keyIndex, out isDev),
|
|
||||||
_ => false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool MatchesSingle(string keyName, out bool isDev)
|
|
||||||
{
|
|
||||||
Assert.Equal((int)KeyRangeType.Single, (int)RangeType);
|
|
||||||
|
|
||||||
isDev = false;
|
|
||||||
|
|
||||||
if (keyName.Length == NameLength + 4)
|
|
||||||
{
|
|
||||||
// Might be a dev key. Check if "_dev" comes after the base key name
|
|
||||||
if (!keyName.AsSpan(Name.Length, 4).SequenceEqual("_dev"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
isDev = true;
|
|
||||||
}
|
|
||||||
else if (keyName.Length != NameLength)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the base name matches
|
|
||||||
if (!keyName.AsSpan(0, Name.Length).SequenceEqual(Name))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool MatchesRangedKey(string keyName, ref int keyIndex, out bool isDev)
|
|
||||||
{
|
|
||||||
Assert.Equal((int)KeyRangeType.Range, (int)RangeType);
|
|
||||||
|
|
||||||
isDev = false;
|
|
||||||
|
|
||||||
// Check if this is an explicit dev key
|
|
||||||
if (keyName.Length == Name.Length + 7)
|
|
||||||
{
|
|
||||||
// Check if "_dev" comes after the base key name
|
|
||||||
if (!keyName.AsSpan(Name.Length, 4).SequenceEqual("_dev"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
isDev = true;
|
|
||||||
}
|
|
||||||
// Not a dev key. Check that the length of the key name with the trailing index matches
|
|
||||||
else if (keyName.Length != Name.Length + 3)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if the name before the "_XX" index matches
|
|
||||||
if (!keyName.AsSpan(0, Name.Length).SequenceEqual(Name))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// The name should have an underscore before the index value
|
|
||||||
if (keyName[keyName.Length - 3] != '_')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
byte index = default;
|
|
||||||
|
|
||||||
// Try to get the index of the key name
|
|
||||||
if (!keyName.AsSpan(keyName.Length - 2, 2).TryToBytes(SpanHelpers.AsSpan(ref index)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if the index is in this key's range
|
|
||||||
if (index < RangeStart || index >= RangeEnd)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
keyIndex = index;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsKeyTypeValid(KeyType type)
|
|
||||||
{
|
|
||||||
// Make sure the type has exactly one flag set for each type
|
|
||||||
KeyType type1 = type & (KeyType.Common | KeyType.Device);
|
|
||||||
KeyType type2 = type & (KeyType.Root | KeyType.Seed | KeyType.Derived);
|
|
||||||
|
|
||||||
bool isValid1 = type1 == KeyType.Common || type1 == KeyType.Device;
|
|
||||||
bool isValid2 = type2 == KeyType.Root || type2 == KeyType.Seed || type2 == KeyType.Derived;
|
|
||||||
|
|
||||||
return isValid1 && isValid2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains info from a specific key being read from a file
|
// Contains info from a specific key being read from a file
|
||||||
[DebuggerDisplay("{" + nameof(Name) + "}")]
|
[DebuggerDisplay("{" + nameof(Name) + "}")]
|
||||||
private struct SpecificKeyInfo
|
private struct SpecificKeyInfo
|
||||||
|
@ -164,32 +27,6 @@ namespace LibHac.Common.Keys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum KeyRangeType : byte
|
|
||||||
{
|
|
||||||
Single,
|
|
||||||
Range
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum KeyType : byte
|
|
||||||
{
|
|
||||||
Common = 1 << 0,
|
|
||||||
Device = 1 << 1,
|
|
||||||
Root = 1 << 2,
|
|
||||||
Seed = 1 << 3,
|
|
||||||
Derived = 1 << 4,
|
|
||||||
|
|
||||||
/// <summary>Specifies that a seed is different in prod and dev.</summary>
|
|
||||||
DifferentDev = 1 << 5,
|
|
||||||
|
|
||||||
CommonRoot = Common | Root,
|
|
||||||
CommonSeed = Common | Seed,
|
|
||||||
CommonSeedDiff = Common | Seed | DifferentDev,
|
|
||||||
CommonDrvd = Common | Derived,
|
|
||||||
DeviceRoot = Device | Root,
|
|
||||||
DeviceDrvd = Device | Derived,
|
|
||||||
}
|
|
||||||
|
|
||||||
private const int TitleKeySize = 0x10;
|
private const int TitleKeySize = 0x10;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -204,7 +41,7 @@ namespace LibHac.Common.Keys
|
||||||
public static void ReadKeyFile(KeySet keySet, string filename, string titleKeysFilename = null,
|
public static void ReadKeyFile(KeySet keySet, string filename, string titleKeysFilename = null,
|
||||||
string consoleKeysFilename = null, IProgressReport logger = null)
|
string consoleKeysFilename = null, IProgressReport logger = null)
|
||||||
{
|
{
|
||||||
List<KeyInfo> keyInfos = CreateKeyList();
|
List<KeyInfo> keyInfos = DefaultKeySet.CreateKeyList();
|
||||||
|
|
||||||
if (filename != null)
|
if (filename != null)
|
||||||
{
|
{
|
||||||
|
@ -263,7 +100,7 @@ namespace LibHac.Common.Keys
|
||||||
/// <param name="keySet">The <see cref="KeySet"/> where the loaded keys will be placed.</param>
|
/// <param name="keySet">The <see cref="KeySet"/> where the loaded keys will be placed.</param>
|
||||||
/// <param name="keyFileReader">A <see cref="TextReader"/> containing the keys to load.</param>
|
/// <param name="keyFileReader">A <see cref="TextReader"/> containing the keys to load.</param>
|
||||||
/// <param name="keyList">A list of all the keys that will be loaded into the key set.
|
/// <param name="keyList">A list of all the keys that will be loaded into the key set.
|
||||||
/// <see cref="CreateKeyList"/> will create a list containing all loadable keys.</param>
|
/// <see cref="DefaultKeySet.CreateKeyList"/> will create a list containing all loadable keys.</param>
|
||||||
/// <param name="logger">An optional logger that key-parsing errors will be written to.</param>
|
/// <param name="logger">An optional logger that key-parsing errors will be written to.</param>
|
||||||
public static void ReadMainKeys(KeySet keySet, TextReader keyFileReader, List<KeyInfo> keyList,
|
public static void ReadMainKeys(KeySet keySet, TextReader keyFileReader, List<KeyInfo> keyList,
|
||||||
IProgressReport logger = null)
|
IProgressReport logger = null)
|
||||||
|
@ -386,259 +223,5 @@ namespace LibHac.Common.Keys
|
||||||
info = default;
|
info = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PrintKeys(KeySet keySet, StringBuilder sb, List<KeyInfo> keys, KeyType filter, bool isDev)
|
|
||||||
{
|
|
||||||
if (keys.Count == 0) return;
|
|
||||||
|
|
||||||
string devSuffix = isDev ? "_dev" : string.Empty;
|
|
||||||
int maxNameLength = keys.Max(x => x.NameLength);
|
|
||||||
int currentGroup = 0;
|
|
||||||
|
|
||||||
// Todo: Better filtering
|
|
||||||
bool FilterMatches(KeyInfo keyInfo)
|
|
||||||
{
|
|
||||||
KeyType filter1 = filter & (KeyType.Common | KeyType.Device);
|
|
||||||
KeyType filter2 = filter & (KeyType.Root | KeyType.Seed | KeyType.Derived);
|
|
||||||
KeyType filter3 = filter & KeyType.DifferentDev;
|
|
||||||
|
|
||||||
// Skip sub-filters that have no flags set
|
|
||||||
return (filter1 == 0 || (filter1 & keyInfo.Type) != 0) &&
|
|
||||||
(filter2 == 0 || (filter2 & keyInfo.Type) != 0) &&
|
|
||||||
filter3 == (filter3 & keyInfo.Type) ||
|
|
||||||
isDev && keyInfo.Type.HasFlag(KeyType.DifferentDev);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFirstPrint = true;
|
|
||||||
|
|
||||||
foreach (KeyInfo info in keys.Where(x => x.Group >= 0).Where(FilterMatches)
|
|
||||||
.OrderBy(x => x.Group).ThenBy(x => x.Name))
|
|
||||||
{
|
|
||||||
bool isNewGroup = false;
|
|
||||||
|
|
||||||
if (info.Group == currentGroup + 1)
|
|
||||||
{
|
|
||||||
currentGroup = info.Group;
|
|
||||||
}
|
|
||||||
else if (info.Group > currentGroup)
|
|
||||||
{
|
|
||||||
// Don't update the current group yet because if this key is empty and the next key
|
|
||||||
// is in the same group, we need to be able to know to add a blank line before printing it.
|
|
||||||
isNewGroup = !isFirstPrint;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.RangeType == KeyRangeType.Single)
|
|
||||||
{
|
|
||||||
Span<byte> key = info.Getter(keySet, 0);
|
|
||||||
if (key.IsEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (isNewGroup)
|
|
||||||
{
|
|
||||||
sb.AppendLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
string keyName = $"{info.Name}{devSuffix}";
|
|
||||||
|
|
||||||
string line = $"{keyName.PadRight(maxNameLength)} = {key.ToHexString()}";
|
|
||||||
sb.AppendLine(line);
|
|
||||||
isFirstPrint = false;
|
|
||||||
currentGroup = info.Group;
|
|
||||||
}
|
|
||||||
else if (info.RangeType == KeyRangeType.Range)
|
|
||||||
{
|
|
||||||
bool hasPrintedKey = false;
|
|
||||||
|
|
||||||
for (int i = info.RangeStart; i < info.RangeEnd; i++)
|
|
||||||
{
|
|
||||||
Span<byte> key = info.Getter(keySet, i);
|
|
||||||
if (key.IsEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (hasPrintedKey == false)
|
|
||||||
{
|
|
||||||
if (isNewGroup)
|
|
||||||
{
|
|
||||||
sb.AppendLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPrintedKey = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
string keyName = $"{info.Name}{devSuffix}_{i:x2}";
|
|
||||||
|
|
||||||
string line = $"{keyName.PadRight(maxNameLength)} = {key.ToHexString()}";
|
|
||||||
sb.AppendLine(line);
|
|
||||||
isFirstPrint = false;
|
|
||||||
currentGroup = info.Group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string PrintTitleKeys(KeySet keySet)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
|
|
||||||
foreach ((RightsId rightsId, AccessKey key) kv in keySet.ExternalKeySet.ToList()
|
|
||||||
.OrderBy(x => x.rightsId.ToString()))
|
|
||||||
{
|
|
||||||
string line = $"{kv.rightsId} = {kv.key}";
|
|
||||||
sb.AppendLine(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string PrintCommonKeys(KeySet keySet)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
PrintKeys(keySet, sb, CreateKeyList(), KeyType.Common | KeyType.Root | KeyType.Seed | KeyType.Derived, false);
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string PrintDeviceKeys(KeySet keySet)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
PrintKeys(keySet, sb, CreateKeyList(), KeyType.Device, false);
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string PrintAllKeys(KeySet keySet)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
PrintKeys(keySet, sb, CreateKeyList(), 0, false);
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string PrintAllSeeds(KeySet keySet)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
PrintKeys(keySet, sb, CreateKeyList(), KeyType.Common | KeyType.Seed, false);
|
|
||||||
|
|
||||||
if (keySet.CurrentMode == KeySet.Mode.Prod)
|
|
||||||
{
|
|
||||||
sb.AppendLine();
|
|
||||||
keySet.SetMode(KeySet.Mode.Dev);
|
|
||||||
PrintKeys(keySet, sb, CreateKeyList(), KeyType.Common | KeyType.Seed | KeyType.DifferentDev, true);
|
|
||||||
keySet.SetMode(KeySet.Mode.Prod);
|
|
||||||
}
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string PrintCommonKeysWithDev(KeySet keySet)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
PrintKeys(keySet, sb, CreateKeyList(), KeyType.Common | KeyType.Root | KeyType.Seed | KeyType.Derived, false);
|
|
||||||
|
|
||||||
if (keySet.CurrentMode == KeySet.Mode.Prod)
|
|
||||||
{
|
|
||||||
sb.AppendLine();
|
|
||||||
keySet.SetMode(KeySet.Mode.Dev);
|
|
||||||
PrintKeys(keySet, sb, CreateKeyList(), KeyType.Common | KeyType.Root | KeyType.Derived, true);
|
|
||||||
keySet.SetMode(KeySet.Mode.Prod);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<KeyInfo> CreateKeyList()
|
|
||||||
{
|
|
||||||
// Update this value if more keys are added
|
|
||||||
var keys = new List<KeyInfo>(70);
|
|
||||||
|
|
||||||
// Keys with a group value of -1 are keys that will be read but not written.
|
|
||||||
// This is for compatibility since some keys had other names in the past.
|
|
||||||
|
|
||||||
// TSEC secrets aren't public yet, so the TSEC root keys will be treated as
|
|
||||||
// root keys even though they're derived.
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(10, KeyType.DeviceRoot, "secure_boot_key", (set, i) => set.SecureBootKey));
|
|
||||||
keys.Add(new KeyInfo(11, KeyType.DeviceRoot, "tsec_key", (set, i) => set.TsecKey));
|
|
||||||
keys.Add(new KeyInfo(12, KeyType.DeviceDrvd, "device_key", (set, i) => set.DeviceKey));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(20, KeyType.CommonRoot, "tsec_root_kek", (set, i) => set.TsecRootKek));
|
|
||||||
keys.Add(new KeyInfo(21, KeyType.CommonRoot, "package1_mac_kek", (set, i) => set.Package1MacKek));
|
|
||||||
keys.Add(new KeyInfo(22, KeyType.CommonRoot, "package1_kek", (set, i) => set.Package1Kek));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(30, KeyType.CommonRoot, "tsec_auth_signature", 0, 0x20, (set, i) => set.TsecAuthSignatures[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(40, KeyType.CommonRoot, "tsec_root_key", 0, 0x20, (set, i) => set.TsecRootKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(50, KeyType.CommonSeed, "keyblob_mac_key_source", (set, i) => set.KeyBlobMacKeySource));
|
|
||||||
keys.Add(new KeyInfo(51, KeyType.CommonSeed, "keyblob_key_source", 0, 6, (set, i) => set.KeyBlobKeySources[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(55, KeyType.DeviceDrvd, "keyblob_key", 0, 6, (set, i) => set.KeyBlobKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(60, KeyType.DeviceDrvd, "keyblob_mac_key", 0, 6, (set, i) => set.KeyBlobMacKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(70, KeyType.DeviceRoot, "encrypted_keyblob", 0, 6, (set, i) => set.EncryptedKeyBlobs[i].Bytes));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(80, KeyType.CommonRoot, "keyblob", 0, 6, (set, i) => set.KeyBlobs[i].Bytes));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(90, KeyType.CommonSeed, "master_kek_source", 6, 0x20, (set, i) => set.MasterKekSources[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(100, KeyType.CommonRoot, "mariko_bek", (set, i) => set.MarikoBek));
|
|
||||||
keys.Add(new KeyInfo(101, KeyType.CommonRoot, "mariko_kek", (set, i) => set.MarikoKek));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(110, KeyType.CommonRoot, "mariko_aes_class_key", 0, 0xC, (set, i) => set.MarikoAesClassKeys[i]));
|
|
||||||
keys.Add(new KeyInfo(120, KeyType.CommonSeedDiff, "mariko_master_kek_source", 0, 0x20, (set, i) => set.MarikoMasterKekSources[i]));
|
|
||||||
keys.Add(new KeyInfo(130, KeyType.CommonDrvd, "master_kek", 0, 0x20, (set, i) => set.MasterKeks[i]));
|
|
||||||
keys.Add(new KeyInfo(140, KeyType.CommonSeed, "master_key_source", (set, i) => set.MasterKeySource));
|
|
||||||
keys.Add(new KeyInfo(150, KeyType.CommonDrvd, "master_key", 0, 0x20, (set, i) => set.MasterKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(160, KeyType.CommonDrvd, "package1_key", 0, 0x20, (set, i) => set.Package1Keys[i]));
|
|
||||||
keys.Add(new KeyInfo(170, KeyType.CommonDrvd, "package1_mac_key", 6, 0x20, (set, i) => set.Package1MacKeys[i]));
|
|
||||||
keys.Add(new KeyInfo(180, KeyType.CommonSeed, "package2_key_source", (set, i) => set.Package2KeySource));
|
|
||||||
keys.Add(new KeyInfo(190, KeyType.CommonDrvd, "package2_key", 0, 0x20, (set, i) => set.Package2Keys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(200, KeyType.CommonSeed, "bis_kek_source", (set, i) => set.BisKekSource));
|
|
||||||
keys.Add(new KeyInfo(201, KeyType.CommonSeed, "bis_key_source", 0, 4, (set, i) => set.BisKeySources[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(205, KeyType.DeviceDrvd, "bis_key", 0, 4, (set, i) => set.BisKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(210, KeyType.CommonSeed, "per_console_key_source", (set, i) => set.PerConsoleKeySource));
|
|
||||||
keys.Add(new KeyInfo(211, KeyType.CommonSeed, "retail_specific_aes_key_source", (set, i) => set.RetailSpecificAesKeySource));
|
|
||||||
keys.Add(new KeyInfo(212, KeyType.CommonSeed, "aes_kek_generation_source", (set, i) => set.AesKekGenerationSource));
|
|
||||||
keys.Add(new KeyInfo(213, KeyType.CommonSeed, "aes_key_generation_source", (set, i) => set.AesKeyGenerationSource));
|
|
||||||
keys.Add(new KeyInfo(214, KeyType.CommonSeed, "titlekek_source", (set, i) => set.TitleKekSource));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(220, KeyType.CommonDrvd, "titlekek", 0, 0x20, (set, i) => set.TitleKeks[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(230, KeyType.CommonSeed, "header_kek_source", (set, i) => set.HeaderKekSource));
|
|
||||||
keys.Add(new KeyInfo(231, KeyType.CommonSeed, "header_key_source", (set, i) => set.HeaderKeySource));
|
|
||||||
keys.Add(new KeyInfo(232, KeyType.CommonDrvd, "header_key", (set, i) => set.HeaderKey));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(240, KeyType.CommonSeed, "key_area_key_application_source", (set, i) => set.KeyAreaKeyApplicationSource));
|
|
||||||
keys.Add(new KeyInfo(241, KeyType.CommonSeed, "key_area_key_ocean_source", (set, i) => set.KeyAreaKeyOceanSource));
|
|
||||||
keys.Add(new KeyInfo(242, KeyType.CommonSeed, "key_area_key_system_source", (set, i) => set.KeyAreaKeySystemSource));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(250, KeyType.CommonSeed, "save_mac_kek_source", (set, i) => set.DeviceUniqueSaveMacKekSource));
|
|
||||||
keys.Add(new KeyInfo(251, KeyType.CommonSeed, "save_mac_key_source", 0, 2, (set, i) => set.DeviceUniqueSaveMacKeySources[i]));
|
|
||||||
keys.Add(new KeyInfo(252, KeyType.DeviceDrvd, "save_mac_key", 0, 2, (set, i) => set.DeviceUniqueSaveMacKeys[i]));
|
|
||||||
keys.Add(new KeyInfo(-01, KeyType.CommonSeed, "save_mac_key_source", (set, i) => set.DeviceUniqueSaveMacKeySources[0]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(253, KeyType.CommonSeed, "save_mac_sd_card_kek_source", (set, i) => set.SeedUniqueSaveMacKekSource));
|
|
||||||
keys.Add(new KeyInfo(254, KeyType.CommonSeed, "save_mac_sd_card_key_source", (set, i) => set.SeedUniqueSaveMacKeySource));
|
|
||||||
keys.Add(new KeyInfo(255, KeyType.DeviceDrvd, "save_mac_sd_card_key", (set, i) => set.SeedUniqueSaveMacKey));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(260, KeyType.DeviceRoot, "sd_seed", (set, i) => set.SdCardEncryptionSeed));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(261, KeyType.CommonSeed, "sd_card_kek_source", (set, i) => set.SdCardKekSource));
|
|
||||||
keys.Add(new KeyInfo(262, KeyType.CommonSeed, "sd_card_save_key_source", (set, i) => set.SdCardKeySources[0]));
|
|
||||||
keys.Add(new KeyInfo(263, KeyType.CommonSeed, "sd_card_nca_key_source", (set, i) => set.SdCardKeySources[1]));
|
|
||||||
keys.Add(new KeyInfo(264, KeyType.CommonSeed, "sd_card_custom_storage_key_source", (set, i) => set.SdCardKeySources[2]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(270, KeyType.CommonSeedDiff, "xci_header_key", (set, i) => set.XciHeaderKey));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(280, KeyType.CommonRoot, "eticket_rsa_kek", (set, i) => set.ETicketRsaKek));
|
|
||||||
keys.Add(new KeyInfo(281, KeyType.CommonRoot, "ssl_rsa_kek", (set, i) => set.SslRsaKek));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(290, KeyType.CommonDrvd, "key_area_key_application", 0, 0x20, (set, i) => set.KeyAreaKeys[i][0]));
|
|
||||||
keys.Add(new KeyInfo(300, KeyType.CommonDrvd, "key_area_key_ocean", 0, 0x20, (set, i) => set.KeyAreaKeys[i][1]));
|
|
||||||
keys.Add(new KeyInfo(310, KeyType.CommonDrvd, "key_area_key_system", 0, 0x20, (set, i) => set.KeyAreaKeys[i][2]));
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
173
src/LibHac/Common/Keys/ExternalKeyWriter.cs
Normal file
173
src/LibHac/Common/Keys/ExternalKeyWriter.cs
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Spl;
|
||||||
|
|
||||||
|
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
|
||||||
|
using RangeType = LibHac.Common.Keys.KeyInfo.KeyRangeType;
|
||||||
|
|
||||||
|
namespace LibHac.Common.Keys
|
||||||
|
{
|
||||||
|
public static class ExternalKeyWriter
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void PrintKeys(KeySet keySet, StringBuilder sb, List<KeyInfo> keys, Type filter, bool isDev)
|
||||||
|
{
|
||||||
|
if (keys.Count == 0) return;
|
||||||
|
|
||||||
|
string devSuffix = isDev ? "_dev" : string.Empty;
|
||||||
|
int maxNameLength = keys.Max(x => x.NameLength);
|
||||||
|
int currentGroup = 0;
|
||||||
|
|
||||||
|
// Todo: Better filtering
|
||||||
|
bool FilterMatches(KeyInfo keyInfo)
|
||||||
|
{
|
||||||
|
Type filter1 = filter & (Type.Common | Type.Device);
|
||||||
|
Type filter2 = filter & (Type.Root | Type.Seed | Type.Derived);
|
||||||
|
Type filter3 = filter & Type.DifferentDev;
|
||||||
|
|
||||||
|
// Skip sub-filters that have no flags set
|
||||||
|
return (filter1 == 0 || (filter1 & keyInfo.Type) != 0) &&
|
||||||
|
(filter2 == 0 || (filter2 & keyInfo.Type) != 0) &&
|
||||||
|
filter3 == (filter3 & keyInfo.Type) ||
|
||||||
|
isDev && keyInfo.Type.HasFlag(Type.DifferentDev);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFirstPrint = true;
|
||||||
|
|
||||||
|
foreach (KeyInfo info in keys.Where(x => x.Group >= 0).Where(FilterMatches)
|
||||||
|
.OrderBy(x => x.Group).ThenBy(x => x.Name))
|
||||||
|
{
|
||||||
|
bool isNewGroup = false;
|
||||||
|
|
||||||
|
if (info.Group == currentGroup + 1)
|
||||||
|
{
|
||||||
|
currentGroup = info.Group;
|
||||||
|
}
|
||||||
|
else if (info.Group > currentGroup)
|
||||||
|
{
|
||||||
|
// Don't update the current group yet because if this key is empty and the next key
|
||||||
|
// is in the same group, we need to be able to know to add a blank line before printing it.
|
||||||
|
isNewGroup = !isFirstPrint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.RangeType == RangeType.Single)
|
||||||
|
{
|
||||||
|
Span<byte> key = info.Getter(keySet, 0);
|
||||||
|
if (key.IsEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (isNewGroup)
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
string keyName = $"{info.Name}{devSuffix}";
|
||||||
|
|
||||||
|
string line = $"{keyName.PadRight(maxNameLength)} = {key.ToHexString()}";
|
||||||
|
sb.AppendLine(line);
|
||||||
|
isFirstPrint = false;
|
||||||
|
currentGroup = info.Group;
|
||||||
|
}
|
||||||
|
else if (info.RangeType == RangeType.Range)
|
||||||
|
{
|
||||||
|
bool hasPrintedKey = false;
|
||||||
|
|
||||||
|
for (int i = info.RangeStart; i < info.RangeEnd; i++)
|
||||||
|
{
|
||||||
|
Span<byte> key = info.Getter(keySet, i);
|
||||||
|
if (key.IsEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (hasPrintedKey == false)
|
||||||
|
{
|
||||||
|
if (isNewGroup)
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPrintedKey = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string keyName = $"{info.Name}{devSuffix}_{i:x2}";
|
||||||
|
|
||||||
|
string line = $"{keyName.PadRight(maxNameLength)} = {key.ToHexString()}";
|
||||||
|
sb.AppendLine(line);
|
||||||
|
isFirstPrint = false;
|
||||||
|
currentGroup = info.Group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PrintTitleKeys(KeySet keySet)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
foreach ((RightsId rightsId, AccessKey key) kv in keySet.ExternalKeySet.ToList()
|
||||||
|
.OrderBy(x => x.rightsId.ToString()))
|
||||||
|
{
|
||||||
|
string line = $"{kv.rightsId} = {kv.key}";
|
||||||
|
sb.AppendLine(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PrintCommonKeys(KeySet keySet)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
|
||||||
|
false);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PrintDeviceKeys(KeySet keySet)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Device, false);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PrintAllKeys(KeySet keySet)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), 0, false);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PrintAllSeeds(KeySet keySet)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed, false);
|
||||||
|
|
||||||
|
if (keySet.CurrentMode == KeySet.Mode.Prod)
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
keySet.SetMode(KeySet.Mode.Dev);
|
||||||
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed | Type.DifferentDev, true);
|
||||||
|
keySet.SetMode(KeySet.Mode.Prod);
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PrintCommonKeysWithDev(KeySet keySet)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (keySet.CurrentMode == KeySet.Mode.Prod)
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
keySet.SetMode(KeySet.Mode.Dev);
|
||||||
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Derived, true);
|
||||||
|
keySet.SetMode(KeySet.Mode.Prod);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
166
src/LibHac/Common/Keys/KeyInfo.cs
Normal file
166
src/LibHac/Common/Keys/KeyInfo.cs
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using LibHac.Diag;
|
||||||
|
|
||||||
|
namespace LibHac.Common.Keys
|
||||||
|
{
|
||||||
|
[DebuggerDisplay("{" + nameof(Name) + "}")]
|
||||||
|
public readonly struct KeyInfo
|
||||||
|
{
|
||||||
|
public enum KeyRangeType : byte
|
||||||
|
{
|
||||||
|
Single,
|
||||||
|
Range
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum KeyType : byte
|
||||||
|
{
|
||||||
|
Common = 1 << 0,
|
||||||
|
Device = 1 << 1,
|
||||||
|
Root = 1 << 2,
|
||||||
|
Seed = 1 << 3,
|
||||||
|
Derived = 1 << 4,
|
||||||
|
|
||||||
|
/// <summary>Specifies that a seed is different in prod and dev.</summary>
|
||||||
|
DifferentDev = 1 << 5,
|
||||||
|
|
||||||
|
CommonRoot = Common | Root,
|
||||||
|
CommonSeed = Common | Seed,
|
||||||
|
CommonSeedDiff = Common | Seed | DifferentDev,
|
||||||
|
CommonDrvd = Common | Derived,
|
||||||
|
DeviceRoot = Device | Root,
|
||||||
|
DeviceDrvd = Device | Derived,
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly string Name;
|
||||||
|
public readonly KeyGetter Getter;
|
||||||
|
public readonly int Group;
|
||||||
|
public readonly KeyRangeType RangeType;
|
||||||
|
public readonly KeyType Type;
|
||||||
|
public readonly byte RangeStart;
|
||||||
|
public readonly byte RangeEnd;
|
||||||
|
|
||||||
|
public int NameLength => Name.Length + (RangeType == KeyRangeType.Range ? 3 : 0);
|
||||||
|
|
||||||
|
public delegate Span<byte> KeyGetter(KeySet keySet, int i);
|
||||||
|
|
||||||
|
public KeyInfo(int group, KeyType type, string name, KeyGetter retrieveFunc)
|
||||||
|
{
|
||||||
|
Assert.AssertTrue(IsKeyTypeValid(type));
|
||||||
|
|
||||||
|
Name = name;
|
||||||
|
RangeType = KeyRangeType.Single;
|
||||||
|
Type = type;
|
||||||
|
RangeStart = default;
|
||||||
|
RangeEnd = default;
|
||||||
|
Group = group;
|
||||||
|
Getter = retrieveFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyInfo(int group, KeyType type, string name, byte rangeStart, byte rangeEnd, KeyGetter retrieveFunc)
|
||||||
|
{
|
||||||
|
Assert.AssertTrue(IsKeyTypeValid(type));
|
||||||
|
|
||||||
|
Name = name;
|
||||||
|
RangeType = KeyRangeType.Range;
|
||||||
|
Type = type;
|
||||||
|
RangeStart = rangeStart;
|
||||||
|
RangeEnd = rangeEnd;
|
||||||
|
Group = group;
|
||||||
|
Getter = retrieveFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Matches(string keyName, out int keyIndex, out bool isDev)
|
||||||
|
{
|
||||||
|
keyIndex = default;
|
||||||
|
isDev = default;
|
||||||
|
|
||||||
|
return RangeType switch
|
||||||
|
{
|
||||||
|
KeyRangeType.Single => MatchesSingle(keyName, out isDev),
|
||||||
|
KeyRangeType.Range => MatchesRangedKey(keyName, ref keyIndex, out isDev),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesSingle(string keyName, out bool isDev)
|
||||||
|
{
|
||||||
|
Assert.Equal((int)KeyRangeType.Single, (int)RangeType);
|
||||||
|
|
||||||
|
isDev = false;
|
||||||
|
|
||||||
|
if (keyName.Length == NameLength + 4)
|
||||||
|
{
|
||||||
|
// Might be a dev key. Check if "_dev" comes after the base key name
|
||||||
|
if (!keyName.AsSpan(Name.Length, 4).SequenceEqual("_dev"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
isDev = true;
|
||||||
|
}
|
||||||
|
else if (keyName.Length != NameLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the base name matches
|
||||||
|
if (!keyName.AsSpan(0, Name.Length).SequenceEqual(Name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesRangedKey(string keyName, ref int keyIndex, out bool isDev)
|
||||||
|
{
|
||||||
|
Assert.Equal((int)KeyRangeType.Range, (int)RangeType);
|
||||||
|
|
||||||
|
isDev = false;
|
||||||
|
|
||||||
|
// Check if this is an explicit dev key
|
||||||
|
if (keyName.Length == Name.Length + 7)
|
||||||
|
{
|
||||||
|
// Check if "_dev" comes after the base key name
|
||||||
|
if (!keyName.AsSpan(Name.Length, 4).SequenceEqual("_dev"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
isDev = true;
|
||||||
|
}
|
||||||
|
// Not a dev key. Check that the length of the key name with the trailing index matches
|
||||||
|
else if (keyName.Length != Name.Length + 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the name before the "_XX" index matches
|
||||||
|
if (!keyName.AsSpan(0, Name.Length).SequenceEqual(Name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The name should have an underscore before the index value
|
||||||
|
if (keyName[keyName.Length - 3] != '_')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
byte index = default;
|
||||||
|
|
||||||
|
// Try to get the index of the key name
|
||||||
|
if (!keyName.AsSpan(keyName.Length - 2, 2).TryToBytes(SpanHelpers.AsSpan(ref index)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the index is in this key's range
|
||||||
|
if (index < RangeStart || index >= RangeEnd)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
keyIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsKeyTypeValid(KeyType type)
|
||||||
|
{
|
||||||
|
// Make sure the type has exactly one flag set for each type
|
||||||
|
KeyType type1 = type & (KeyType.Common | KeyType.Device);
|
||||||
|
KeyType type2 = type & (KeyType.Root | KeyType.Seed | KeyType.Derived);
|
||||||
|
|
||||||
|
bool isValid1 = type1 == KeyType.Common || type1 == KeyType.Device;
|
||||||
|
bool isValid2 = type2 == KeyType.Root || type2 == KeyType.Seed || type2 == KeyType.Derived;
|
||||||
|
|
||||||
|
return isValid1 && isValid2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -208,7 +208,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
private static void ProcessKeygen(Context ctx)
|
private static void ProcessKeygen(Context ctx)
|
||||||
{
|
{
|
||||||
Console.WriteLine(ExternalKeyReader.PrintAllKeys(ctx.KeySet));
|
Console.WriteLine(ExternalKeyWriter.PrintAllKeys(ctx.KeySet));
|
||||||
|
|
||||||
if (ctx.Options.OutDir != null)
|
if (ctx.Options.OutDir != null)
|
||||||
{
|
{
|
||||||
|
@ -216,14 +216,14 @@ namespace hactoolnet
|
||||||
string dir = ctx.Options.OutDir;
|
string dir = ctx.Options.OutDir;
|
||||||
Directory.CreateDirectory(dir);
|
Directory.CreateDirectory(dir);
|
||||||
|
|
||||||
File.WriteAllText(Path.Combine(dir, keyFileName), ExternalKeyReader.PrintCommonKeys(ctx.KeySet));
|
File.WriteAllText(Path.Combine(dir, keyFileName), ExternalKeyWriter.PrintCommonKeys(ctx.KeySet));
|
||||||
File.WriteAllText(Path.Combine(dir, "console.keys"), ExternalKeyReader.PrintDeviceKeys(ctx.KeySet));
|
File.WriteAllText(Path.Combine(dir, "console.keys"), ExternalKeyWriter.PrintDeviceKeys(ctx.KeySet));
|
||||||
File.WriteAllText(Path.Combine(dir, "title.keys"), ExternalKeyReader.PrintTitleKeys(ctx.KeySet));
|
File.WriteAllText(Path.Combine(dir, "title.keys"), ExternalKeyWriter.PrintTitleKeys(ctx.KeySet));
|
||||||
|
|
||||||
if (!ctx.Options.UseDevKeys)
|
if (!ctx.Options.UseDevKeys)
|
||||||
{
|
{
|
||||||
File.WriteAllText(Path.Combine(dir, "prod+dev.keys"),
|
File.WriteAllText(Path.Combine(dir, "prod+dev.keys"),
|
||||||
ExternalKeyReader.PrintCommonKeysWithDev(ctx.KeySet));
|
ExternalKeyWriter.PrintCommonKeysWithDev(ctx.KeySet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue