mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Read console-unique keys
This commit is contained in:
parent
7ed92fedcb
commit
ace593a19d
5 changed files with 114 additions and 84 deletions
|
@ -58,6 +58,8 @@ Specifying the patch title ID will extract the patched title.
|
||||||
Keys can be loaded from a text file by specifying a filename with the `-k` argument. The file should be in the same format read by [hactool](https://github.com/SciresM/hactool#external-keys):
|
Keys can be loaded from a text file by specifying a filename with the `-k` argument. The file should be in the same format read by [hactool](https://github.com/SciresM/hactool#external-keys):
|
||||||
"Keyset files are text files containing one key per line, in the form "key_name = HEXADECIMALKEY". Case shouldn't matter, nor should whitespace."
|
"Keyset files are text files containing one key per line, in the form "key_name = HEXADECIMALKEY". Case shouldn't matter, nor should whitespace."
|
||||||
|
|
||||||
|
Console-unique keys can be loaded from a text file by specifying a filename with the `--consolekeys` argument. The file format is the same as the main keyset file.
|
||||||
|
|
||||||
Title keys can be loaded from a text file by specifying a filename with the `--titlekeys` argument. The file should contain one key per line in the form `rights_id,HEXADECIMALKEY`.
|
Title keys can be loaded from a text file by specifying a filename with the `--titlekeys` argument. The file should contain one key per line in the form `rights_id,HEXADECIMALKEY`.
|
||||||
|
|
||||||
If a keyfile is not set at the command line, hactoolnet will search for and load keyfiles in `$HOME/.switch/prod.keys` and `$HOME/.switch/titlekeys.txt`.
|
If a keyfile is not set at the command line, hactoolnet will search for and load keyfiles in `$HOME/.switch/prod.keys`, `$HOME/.switch/console.keys` and `$HOME/.switch/title.keys`.
|
|
@ -15,6 +15,7 @@ namespace hactoolnet
|
||||||
new CliOption("verify", 'y', 0, (o, a) => o.Validate = true),
|
new CliOption("verify", 'y', 0, (o, a) => o.Validate = true),
|
||||||
new CliOption("keyset", 'k', 1, (o, a) => o.Keyfile = a[0]),
|
new CliOption("keyset", 'k', 1, (o, a) => o.Keyfile = a[0]),
|
||||||
new CliOption("titlekeys", 1, (o, a) => o.TitleKeyFile = a[0]),
|
new CliOption("titlekeys", 1, (o, a) => o.TitleKeyFile = a[0]),
|
||||||
|
new CliOption("consolekeys", 1, (o, a) => o.ConsoleKeyFile = a[0]),
|
||||||
new CliOption("section0", 1, (o, a) => o.SectionOut[0] = a[0]),
|
new CliOption("section0", 1, (o, a) => o.SectionOut[0] = a[0]),
|
||||||
new CliOption("section1", 1, (o, a) => o.SectionOut[1] = a[0]),
|
new CliOption("section1", 1, (o, a) => o.SectionOut[1] = a[0]),
|
||||||
new CliOption("section2", 1, (o, a) => o.SectionOut[2] = a[0]),
|
new CliOption("section2", 1, (o, a) => o.SectionOut[2] = a[0]),
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace hactoolnet
|
||||||
public bool Validate;
|
public bool Validate;
|
||||||
public string Keyfile;
|
public string Keyfile;
|
||||||
public string TitleKeyFile;
|
public string TitleKeyFile;
|
||||||
|
public string ConsoleKeyFile;
|
||||||
public string[] SectionOut = new string[4];
|
public string[] SectionOut = new string[4];
|
||||||
public string[] SectionOutDir = new string[4];
|
public string[] SectionOutDir = new string[4];
|
||||||
public string ExefsOut;
|
public string ExefsOut;
|
||||||
|
@ -25,7 +26,6 @@ namespace hactoolnet
|
||||||
public bool ListTitles;
|
public bool ListTitles;
|
||||||
public bool ListRomFs;
|
public bool ListRomFs;
|
||||||
public ulong TitleId;
|
public ulong TitleId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum FileType
|
internal enum FileType
|
||||||
|
|
|
@ -178,9 +178,11 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||||
var homeKeyFile = Path.Combine(home, ".switch", "prod.keys");
|
var homeKeyFile = Path.Combine(home, ".switch", "prod.keys");
|
||||||
var homeTitleKeyFile = Path.Combine(home, ".switch", "titlekeys.txt");
|
var homeTitleKeyFile = Path.Combine(home, ".switch", "title.keys");
|
||||||
|
var homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys");
|
||||||
var keyFile = ctx.Options.Keyfile;
|
var keyFile = ctx.Options.Keyfile;
|
||||||
var titleKeyFile = ctx.Options.TitleKeyFile;
|
var titleKeyFile = ctx.Options.TitleKeyFile;
|
||||||
|
var consoleKeyFile = ctx.Options.ConsoleKeyFile;
|
||||||
|
|
||||||
if (keyFile == null && File.Exists(homeKeyFile))
|
if (keyFile == null && File.Exists(homeKeyFile))
|
||||||
{
|
{
|
||||||
|
@ -192,7 +194,12 @@ namespace hactoolnet
|
||||||
titleKeyFile = homeTitleKeyFile;
|
titleKeyFile = homeTitleKeyFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Keyset = ExternalKeys.ReadKeyFile(keyFile, titleKeyFile, ctx.Logger);
|
if (consoleKeyFile == null && File.Exists(homeConsoleKeyFile))
|
||||||
|
{
|
||||||
|
consoleKeyFile = homeConsoleKeyFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Keyset = ExternalKeys.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile, ctx.Logger);
|
||||||
if (ctx.Options.SdSeed != null)
|
if (ctx.Options.SdSeed != null)
|
||||||
{
|
{
|
||||||
ctx.Keyset.SetSdSeed(ctx.Options.SdSeed.ToBytes());
|
ctx.Keyset.SetSdSeed(ctx.Options.SdSeed.ToBytes());
|
||||||
|
|
180
libhac/Keyset.cs
180
libhac/Keyset.cs
|
@ -9,8 +9,6 @@ namespace libhac
|
||||||
{
|
{
|
||||||
public class Keyset
|
public class Keyset
|
||||||
{
|
{
|
||||||
public byte[] secure_boot_key { get; set; } = new byte[0x10];
|
|
||||||
public byte[] tsec_key { get; set; } = new byte[0x10];
|
|
||||||
public byte[][] keyblob_keys { get; set; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
public byte[][] keyblob_keys { get; set; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||||
public byte[][] keyblob_mac_keys { get; set; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
public byte[][] keyblob_mac_keys { get; set; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||||
public byte[][] encrypted_keyblobs { get; set; } = Util.CreateJaggedArray<byte[][]>(0x20, 0xB0);
|
public byte[][] encrypted_keyblobs { get; set; } = Util.CreateJaggedArray<byte[][]>(0x20, 0xB0);
|
||||||
|
@ -41,22 +39,21 @@ namespace libhac
|
||||||
public byte[] acid_fixed_key_modulus { get; set; } = new byte[0x100];
|
public byte[] acid_fixed_key_modulus { get; set; } = new byte[0x100];
|
||||||
public byte[] package2_fixed_key_modulus { get; set; } = new byte[0x100];
|
public byte[] package2_fixed_key_modulus { get; set; } = new byte[0x100];
|
||||||
|
|
||||||
|
public byte[] secure_boot_key { get; set; } = new byte[0x10];
|
||||||
|
public byte[] tsec_key { get; set; } = new byte[0x10];
|
||||||
|
public byte[] device_key { get; set; } = new byte[0x10];
|
||||||
|
public byte[][] bis_keys { get; set; } = Util.CreateJaggedArray<byte[][]>(4, 0x20);
|
||||||
|
public byte[] sd_seed { get; set; } = new byte[0x10];
|
||||||
|
|
||||||
public Dictionary<byte[], byte[]> TitleKeys { get; } = new Dictionary<byte[], byte[]>(new ByteArray128BitComparer());
|
public Dictionary<byte[], byte[]> TitleKeys { get; } = new Dictionary<byte[], byte[]>(new ByteArray128BitComparer());
|
||||||
|
|
||||||
public void SetSdSeed(byte[] sdseed)
|
public void SetSdSeed(byte[] sdseed)
|
||||||
{
|
{
|
||||||
for (int k = 0; k < sd_card_key_sources.Length; k++)
|
Array.Copy(sdseed, sd_seed, sd_seed.Length);
|
||||||
{
|
|
||||||
for (int i = 0; i < 0x20; i++)
|
|
||||||
{
|
|
||||||
sd_card_key_sources_specific[k][i] = (byte)(sd_card_key_sources[k][i] ^ sdseed[i & 0xF]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DeriveKeys();
|
DeriveKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeriveKeys()
|
internal void DeriveKeys()
|
||||||
{
|
{
|
||||||
//var cmac = new byte[0x10];
|
//var cmac = new byte[0x10];
|
||||||
//for (int i = 0; i < 0x20; i++)
|
//for (int i = 0; i < 0x20; i++)
|
||||||
|
@ -69,6 +66,14 @@ namespace libhac
|
||||||
var sdKek = new byte[0x10];
|
var sdKek = new byte[0x10];
|
||||||
Crypto.GenerateKek(sdKek, sd_card_kek_source, master_keys[0], aes_kek_generation_source, aes_key_generation_source);
|
Crypto.GenerateKek(sdKek, sd_card_kek_source, master_keys[0], aes_kek_generation_source, aes_key_generation_source);
|
||||||
|
|
||||||
|
for (int k = 0; k < sd_card_key_sources.Length; k++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 0x20; i++)
|
||||||
|
{
|
||||||
|
sd_card_key_sources_specific[k][i] = (byte)(sd_card_key_sources[k][i] ^ sd_seed[i & 0xF]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int k = 0; k < sd_card_key_sources_specific.Length; k++)
|
for (int k = 0; k < sd_card_key_sources_specific.Length; k++)
|
||||||
{
|
{
|
||||||
Crypto.DecryptEcb(sdKek, sd_card_key_sources_specific[k], sd_card_keys[k], 0x20);
|
Crypto.DecryptEcb(sdKek, sd_card_key_sources_specific[k], sd_card_keys[k], 0x20);
|
||||||
|
@ -81,45 +86,21 @@ namespace libhac
|
||||||
private const int TitleKeySize = 0x10;
|
private const int TitleKeySize = 0x10;
|
||||||
private static readonly Dictionary<string, KeyValue> KeyDict = CreateKeyDict();
|
private static readonly Dictionary<string, KeyValue> KeyDict = CreateKeyDict();
|
||||||
|
|
||||||
public static Keyset ReadKeyFile(string filename, string titleKeysFilename, IProgressReport progress = null)
|
public static Keyset ReadKeyFile(string filename, string titleKeysFilename = null, string consoleKeysFilename = null, IProgressReport progress = null)
|
||||||
{
|
{
|
||||||
var keyset = ReadKeyFile(filename, progress);
|
var keyset = new Keyset();
|
||||||
if (titleKeysFilename == null) return keyset;
|
|
||||||
|
|
||||||
using (var reader = new StreamReader(new FileStream(titleKeysFilename, FileMode.Open, FileAccess.Read)))
|
if (filename != null) ReadMainKeys(keyset, filename, progress);
|
||||||
{
|
if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, progress);
|
||||||
string line;
|
if (titleKeysFilename != null) ReadTitleKeys(keyset, titleKeysFilename, progress);
|
||||||
while ((line = reader.ReadLine()) != null)
|
keyset.DeriveKeys();
|
||||||
{
|
|
||||||
var a = line.Split(',');
|
|
||||||
if (a.Length != 2) continue;
|
|
||||||
|
|
||||||
var rightsId = a[0].Trim().ToBytes();
|
|
||||||
var titleKey = a[1].Trim().ToBytes();
|
|
||||||
|
|
||||||
if (rightsId.Length != TitleKeySize)
|
|
||||||
{
|
|
||||||
progress?.LogMessage($"Rights ID {rightsId.ToHexString()} had incorrect size {rightsId.Length}. (Expected {TitleKeySize})");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (titleKey.Length != TitleKeySize)
|
|
||||||
{
|
|
||||||
progress?.LogMessage($"Title key {titleKey.ToHexString()} had incorrect size {titleKey.Length}. (Expected {TitleKeySize})");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyset.TitleKeys[rightsId] = titleKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyset;
|
return keyset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Keyset ReadKeyFile(string filename, IProgressReport progress = null)
|
private static void ReadMainKeys(Keyset keyset, string filename, IProgressReport progress = null)
|
||||||
{
|
{
|
||||||
var keyset = new Keyset();
|
if (filename == null) return;
|
||||||
if (filename == null) return keyset;
|
|
||||||
|
|
||||||
using (var reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read)))
|
using (var reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read)))
|
||||||
{
|
{
|
||||||
|
@ -145,52 +126,91 @@ namespace libhac
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
kv.Assign(keyset, value);
|
var dest = kv.GetKey(keyset);
|
||||||
|
Array.Copy(value, dest, value.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return keyset;
|
private static void ReadTitleKeys(Keyset keyset, string filename, IProgressReport progress = null)
|
||||||
|
{
|
||||||
|
if (filename == null) return;
|
||||||
|
|
||||||
|
using (var reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read)))
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
var a = line.Split(',');
|
||||||
|
if (a.Length != 2) continue;
|
||||||
|
|
||||||
|
var rightsId = a[0].Trim().ToBytes();
|
||||||
|
var titleKey = a[1].Trim().ToBytes();
|
||||||
|
|
||||||
|
if (rightsId.Length != TitleKeySize)
|
||||||
|
{
|
||||||
|
progress?.LogMessage($"Rights ID {rightsId.ToHexString()} had incorrect size {rightsId.Length}. (Expected {TitleKeySize})");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (titleKey.Length != TitleKeySize)
|
||||||
|
{
|
||||||
|
progress?.LogMessage($"Title key {titleKey.ToHexString()} had incorrect size {titleKey.Length}. (Expected {TitleKeySize})");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyset.TitleKeys[rightsId] = titleKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<string, KeyValue> CreateKeyDict()
|
private static Dictionary<string, KeyValue> CreateKeyDict()
|
||||||
{
|
{
|
||||||
var keys = new List<KeyValue>
|
var keys = new List<KeyValue>
|
||||||
{
|
{
|
||||||
new KeyValue("aes_kek_generation_source", 0x10, (set, key) => set.aes_kek_generation_source = key),
|
new KeyValue("aes_kek_generation_source", 0x10, set => set.aes_kek_generation_source),
|
||||||
new KeyValue("aes_key_generation_source", 0x10, (set, key) => set.aes_key_generation_source = key),
|
new KeyValue("aes_key_generation_source", 0x10, set => set.aes_key_generation_source),
|
||||||
new KeyValue("key_area_key_application_source", 0x10, (set, key) => set.key_area_key_application_source = key),
|
new KeyValue("key_area_key_application_source", 0x10, set => set.key_area_key_application_source),
|
||||||
new KeyValue("key_area_key_ocean_source", 0x10, (set, key) => set.key_area_key_ocean_source = key),
|
new KeyValue("key_area_key_ocean_source", 0x10, set => set.key_area_key_ocean_source),
|
||||||
new KeyValue("key_area_key_system_source", 0x10, (set, key) => set.key_area_key_system_source = key),
|
new KeyValue("key_area_key_system_source", 0x10, set => set.key_area_key_system_source),
|
||||||
new KeyValue("titlekek_source", 0x10, (set, key) => set.titlekek_source = key),
|
new KeyValue("titlekek_source", 0x10, set => set.titlekek_source),
|
||||||
new KeyValue("header_kek_source", 0x10, (set, key) => set.header_kek_source = key),
|
new KeyValue("header_kek_source", 0x10, set => set.header_kek_source),
|
||||||
new KeyValue("header_key_source", 0x20, (set, key) => set.encrypted_header_key = key),
|
new KeyValue("header_key_source", 0x20, set => set.encrypted_header_key),
|
||||||
new KeyValue("header_key", 0x20, (set, key) => set.header_key = key),
|
new KeyValue("header_key", 0x20, set => set.header_key),
|
||||||
new KeyValue("encrypted_header_key", 0x20, (set, key) => set.encrypted_header_key = key),
|
new KeyValue("encrypted_header_key", 0x20, set => set.encrypted_header_key),
|
||||||
new KeyValue("package2_key_source", 0x10, (set, key) => set.package2_key_source = key),
|
new KeyValue("package2_key_source", 0x10, set => set.package2_key_source),
|
||||||
new KeyValue("sd_card_kek_source", 0x10, (set, key) => set.sd_card_kek_source = key),
|
new KeyValue("sd_card_kek_source", 0x10, set => set.sd_card_kek_source),
|
||||||
new KeyValue("sd_card_nca_key_source", 0x20, (set, key) => set.sd_card_key_sources[1] = key),
|
new KeyValue("sd_card_nca_key_source", 0x20, set => set.sd_card_key_sources[1]),
|
||||||
new KeyValue("sd_card_save_key_source", 0x20, (set, key) => set.sd_card_key_sources[0] = key),
|
new KeyValue("sd_card_save_key_source", 0x20, set => set.sd_card_key_sources[0]),
|
||||||
new KeyValue("master_key_source", 0x10, (set, key) => set.master_key_source = key),
|
new KeyValue("master_key_source", 0x10, set => set.master_key_source),
|
||||||
new KeyValue("keyblob_mac_key_source", 0x10, (set, key) => set.keyblob_mac_key_source = key),
|
new KeyValue("keyblob_mac_key_source", 0x10, set => set.keyblob_mac_key_source),
|
||||||
new KeyValue("secure_boot_key", 0x10, (set, key) => set.secure_boot_key = key),
|
new KeyValue("secure_boot_key", 0x10, set => set.secure_boot_key),
|
||||||
new KeyValue("tsec_key", 0x10, (set, key) => set.tsec_key = key)
|
new KeyValue("tsec_key", 0x10, set => set.tsec_key),
|
||||||
|
new KeyValue("device_key", 0x10, set => set.device_key),
|
||||||
|
new KeyValue("sd_seed", 0x10, set => set.sd_seed)
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int slot = 0; slot < 0x20; slot++)
|
for (int slot = 0; slot < 0x20; slot++)
|
||||||
{
|
{
|
||||||
int i = slot;
|
int i = slot;
|
||||||
keys.Add(new KeyValue($"keyblob_key_source_{i:D2}", 0x10, (set, key) => set.keyblob_key_sources[i] = key));
|
keys.Add(new KeyValue($"keyblob_key_source_{i:D2}", 0x10, set => set.keyblob_key_sources[i]));
|
||||||
keys.Add(new KeyValue($"keyblob_key_{i:D2}", 0x10, (set, key) => set.keyblob_keys[i] = key));
|
keys.Add(new KeyValue($"keyblob_key_{i:D2}", 0x10, set => set.keyblob_keys[i]));
|
||||||
keys.Add(new KeyValue($"keyblob_mac_key_{i:D2}", 0x10, (set, key) => set.keyblob_mac_keys[i] = key));
|
keys.Add(new KeyValue($"keyblob_mac_key_{i:D2}", 0x10, set => set.keyblob_mac_keys[i]));
|
||||||
keys.Add(new KeyValue($"encrypted_keyblob_{i:D2}", 0xB0, (set, key) => set.encrypted_keyblobs[i] = key));
|
keys.Add(new KeyValue($"encrypted_keyblob_{i:D2}", 0xB0, set => set.encrypted_keyblobs[i]));
|
||||||
keys.Add(new KeyValue($"keyblob_{i:D2}", 0x90, (set, key) => set.keyblobs[i] = key));
|
keys.Add(new KeyValue($"keyblob_{i:D2}", 0x90, set => set.keyblobs[i]));
|
||||||
keys.Add(new KeyValue($"master_key_{i:D2}", 0x10, (set, key) => set.master_keys[i] = key));
|
keys.Add(new KeyValue($"master_key_{i:D2}", 0x10, set => set.master_keys[i]));
|
||||||
keys.Add(new KeyValue($"package1_key_{i:D2}", 0x10, (set, key) => set.package1_keys[i] = key));
|
keys.Add(new KeyValue($"package1_key_{i:D2}", 0x10, set => set.package1_keys[i]));
|
||||||
keys.Add(new KeyValue($"package2_key_{i:D2}", 0x10, (set, key) => set.package2_keys[i] = key));
|
keys.Add(new KeyValue($"package2_key_{i:D2}", 0x10, set => set.package2_keys[i]));
|
||||||
keys.Add(new KeyValue($"titlekek_{i:D2}", 0x10, (set, key) => set.titlekeks[i] = key));
|
keys.Add(new KeyValue($"titlekek_{i:D2}", 0x10, set => set.titlekeks[i]));
|
||||||
keys.Add(new KeyValue($"key_area_key_application_{i:D2}", 0x10, (set, key) => set.key_area_keys[i][0] = key));
|
keys.Add(new KeyValue($"key_area_key_application_{i:D2}", 0x10, set => set.key_area_keys[i][0]));
|
||||||
keys.Add(new KeyValue($"key_area_key_ocean_{i:D2}", 0x10, (set, key) => set.key_area_keys[i][1] = key));
|
keys.Add(new KeyValue($"key_area_key_ocean_{i:D2}", 0x10, set => set.key_area_keys[i][1]));
|
||||||
keys.Add(new KeyValue($"key_area_key_system_{i:D2}", 0x10, (set, key) => set.key_area_keys[i][2] = key));
|
keys.Add(new KeyValue($"key_area_key_system_{i:D2}", 0x10, set => set.key_area_keys[i][2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int slot = 0; slot < 4; slot++)
|
||||||
|
{
|
||||||
|
int i = slot;
|
||||||
|
keys.Add(new KeyValue($"bis_key_{i:D2}", 0x20, set => set.bis_keys[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys.ToDictionary(k => k.Name, k => k);
|
return keys.ToDictionary(k => k.Name, k => k);
|
||||||
|
@ -198,15 +218,15 @@ namespace libhac
|
||||||
|
|
||||||
private class KeyValue
|
private class KeyValue
|
||||||
{
|
{
|
||||||
public string Name;
|
public readonly string Name;
|
||||||
public int Size;
|
public readonly int Size;
|
||||||
public Action<Keyset, byte[]> Assign;
|
public readonly Func<Keyset, byte[]> GetKey;
|
||||||
|
|
||||||
public KeyValue(string name, int size, Action<Keyset, byte[]> assign)
|
public KeyValue(string name, int size, Func<Keyset, byte[]> retrieveFunc)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Size = size;
|
Size = size;
|
||||||
Assign = assign;
|
GetKey = retrieveFunc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue