mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Generate keys from master key + source
This commit is contained in:
parent
0bea29e59e
commit
e7f5702477
4 changed files with 92 additions and 23 deletions
|
@ -95,7 +95,7 @@ namespace hactoolnet
|
||||||
i += option.ArgsNeeded;
|
i += option.ArgsNeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inputSpecified)
|
if (!inputSpecified && options.InFileType != FileType.Keygen)
|
||||||
{
|
{
|
||||||
PrintWithUsage("Input file must be specified");
|
PrintWithUsage("Input file must be specified");
|
||||||
return null;
|
return null;
|
||||||
|
@ -145,7 +145,7 @@ namespace hactoolnet
|
||||||
sb.AppendLine(" -r, --raw Keep raw data, don\'t unpack.");
|
sb.AppendLine(" -r, --raw Keep raw data, don\'t unpack.");
|
||||||
sb.AppendLine(" -y, --verify Verify hashes.");
|
sb.AppendLine(" -y, --verify Verify hashes.");
|
||||||
sb.AppendLine(" -k, --keyset Load keys from an external file.");
|
sb.AppendLine(" -k, --keyset Load keys from an external file.");
|
||||||
sb.AppendLine(" -t, --intype=type Specify input file type [nca, switchfs, save]");
|
sb.AppendLine(" -t, --intype=type Specify input file type [nca, xci, switchfs, save, keygen]");
|
||||||
sb.AppendLine(" --titlekeys <file> Load title keys from an external file.");
|
sb.AppendLine(" --titlekeys <file> Load title keys from an external file.");
|
||||||
sb.AppendLine("NCA options:");
|
sb.AppendLine("NCA options:");
|
||||||
sb.AppendLine(" --section0 <file> Specify Section 0 file path.");
|
sb.AppendLine(" --section0 <file> Specify Section 0 file path.");
|
||||||
|
|
|
@ -43,7 +43,8 @@ namespace hactoolnet
|
||||||
Nax0,
|
Nax0,
|
||||||
Xci,
|
Xci,
|
||||||
SwitchFs,
|
SwitchFs,
|
||||||
Save
|
Save,
|
||||||
|
Keygen
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class Context
|
internal class Context
|
||||||
|
|
|
@ -48,6 +48,9 @@ namespace hactoolnet
|
||||||
case FileType.Xci:
|
case FileType.Xci:
|
||||||
ProcessXci(ctx);
|
ProcessXci(ctx);
|
||||||
break;
|
break;
|
||||||
|
case FileType.Keygen:
|
||||||
|
ProcessKeygen(ctx);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
|
@ -460,6 +463,11 @@ namespace hactoolnet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ProcessKeygen(Context ctx)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ExternalKeys.PrintKeys(ctx.Keyset));
|
||||||
|
}
|
||||||
|
|
||||||
// For running random stuff
|
// For running random stuff
|
||||||
// ReSharper disable once UnusedParameter.Local
|
// ReSharper disable once UnusedParameter.Local
|
||||||
private static void CustomTask(Context ctx)
|
private static void CustomTask(Context ctx)
|
||||||
|
|
100
libhac/Keyset.cs
100
libhac/Keyset.cs
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace libhac
|
namespace libhac
|
||||||
{
|
{
|
||||||
|
@ -59,13 +60,45 @@ namespace libhac
|
||||||
|
|
||||||
internal void DeriveKeys()
|
internal void DeriveKeys()
|
||||||
{
|
{
|
||||||
//var cmac = new byte[0x10];
|
for (int i = 0; i < 0x20; i++)
|
||||||
//for (int i = 0; i < 0x20; i++)
|
{
|
||||||
//{
|
if (master_keys[i].IsEmpty())
|
||||||
// Crypto.DecryptEcb(tsec_key, keyblob_key_sources[i], keyblob_keys[i], 0x10);
|
{
|
||||||
// Crypto.DecryptEcb(secure_boot_key, keyblob_keys[i], keyblob_keys[i], 0x10);
|
continue;
|
||||||
// Crypto.DecryptEcb(keyblob_keys[i], keyblob_mac_key_source, keyblob_mac_keys[i], 0x10);
|
}
|
||||||
//}
|
|
||||||
|
if (!key_area_key_application_source.IsEmpty())
|
||||||
|
{
|
||||||
|
Crypto.GenerateKek(key_area_keys[i][0], key_area_key_application_source, master_keys[i], aes_kek_generation_source, aes_key_generation_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key_area_key_ocean_source.IsEmpty())
|
||||||
|
{
|
||||||
|
Crypto.GenerateKek(key_area_keys[i][1], key_area_key_ocean_source, master_keys[i], aes_kek_generation_source, aes_key_generation_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key_area_key_system_source.IsEmpty())
|
||||||
|
{
|
||||||
|
Crypto.GenerateKek(key_area_keys[i][2], key_area_key_system_source, master_keys[i], aes_kek_generation_source, aes_key_generation_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!titlekek_source.IsEmpty())
|
||||||
|
{
|
||||||
|
Crypto.DecryptEcb(master_keys[i], titlekek_source, titlekeks[i], 0x10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!package2_key_source.IsEmpty())
|
||||||
|
{
|
||||||
|
Crypto.DecryptEcb(master_keys[i], package2_key_source, package2_keys[i], 0x10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!header_kek_source.IsEmpty() && !header_key_source.IsEmpty())
|
||||||
|
{
|
||||||
|
var headerKek = new byte[0x10];
|
||||||
|
Crypto.GenerateKek(headerKek, header_kek_source, master_keys[0], aes_kek_generation_source, aes_key_generation_source);
|
||||||
|
Crypto.DecryptEcb(headerKek, header_key_source, header_key, 0x20);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
@ -88,21 +121,22 @@ namespace libhac
|
||||||
public static class ExternalKeys
|
public static class ExternalKeys
|
||||||
{
|
{
|
||||||
private const int TitleKeySize = 0x10;
|
private const int TitleKeySize = 0x10;
|
||||||
private static readonly Dictionary<string, KeyValue> KeyDict = CreateKeyDict();
|
private static readonly Dictionary<string, KeyValue> CommonKeyDict = CreateCommonKeyDict();
|
||||||
|
private static readonly Dictionary<string, KeyValue> UniqueKeyDict = CreateUniqueKeyDict();
|
||||||
|
|
||||||
public static Keyset ReadKeyFile(string filename, string titleKeysFilename = null, string consoleKeysFilename = null, IProgressReport progress = null)
|
public static Keyset ReadKeyFile(string filename, string titleKeysFilename = null, string consoleKeysFilename = null, IProgressReport progress = null)
|
||||||
{
|
{
|
||||||
var keyset = new Keyset();
|
var keyset = new Keyset();
|
||||||
|
|
||||||
if (filename != null) ReadMainKeys(keyset, filename, progress);
|
if (filename != null) ReadMainKeys(keyset, filename, CommonKeyDict, progress);
|
||||||
if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, progress);
|
if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, UniqueKeyDict, progress);
|
||||||
if (titleKeysFilename != null) ReadTitleKeys(keyset, titleKeysFilename, progress);
|
if (titleKeysFilename != null) ReadTitleKeys(keyset, titleKeysFilename, progress);
|
||||||
keyset.DeriveKeys();
|
keyset.DeriveKeys();
|
||||||
|
|
||||||
return keyset;
|
return keyset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReadMainKeys(Keyset keyset, string filename, IProgressReport progress = null)
|
private static void ReadMainKeys(Keyset keyset, string filename, Dictionary<string, KeyValue> keyDict, IProgressReport logger = null)
|
||||||
{
|
{
|
||||||
if (filename == null) return;
|
if (filename == null) return;
|
||||||
|
|
||||||
|
@ -117,16 +151,16 @@ namespace libhac
|
||||||
var key = a[0].Trim();
|
var key = a[0].Trim();
|
||||||
var valueStr = a[1].Trim();
|
var valueStr = a[1].Trim();
|
||||||
|
|
||||||
if (!KeyDict.TryGetValue(key, out var kv))
|
if (!keyDict.TryGetValue(key, out var kv))
|
||||||
{
|
{
|
||||||
progress?.LogMessage($"Failed to match key {key}");
|
logger?.LogMessage($"Failed to match key {key}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = valueStr.ToBytes();
|
var value = valueStr.ToBytes();
|
||||||
if (value.Length != kv.Size)
|
if (value.Length != kv.Size)
|
||||||
{
|
{
|
||||||
progress?.LogMessage($"Key {key} had incorrect size {value.Length}. (Expected {kv.Size})");
|
logger?.LogMessage($"Key {key} had incorrect size {value.Length}. (Expected {kv.Size})");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +202,24 @@ namespace libhac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<string, KeyValue> CreateKeyDict()
|
public static string PrintKeys(Keyset keyset)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
int maxNameLength = CommonKeyDict.Values.Max(x => x.Name.Length);
|
||||||
|
|
||||||
|
foreach (KeyValue keySlot in CommonKeyDict.Values.OrderBy(x => x.Name))
|
||||||
|
{
|
||||||
|
byte[] key = keySlot.GetKey(keyset);
|
||||||
|
if (key.IsEmpty()) continue;
|
||||||
|
|
||||||
|
var line = $"{keySlot.Name.PadRight(maxNameLength)} = {key.ToHexString()}";
|
||||||
|
sb.AppendLine(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, KeyValue> CreateCommonKeyDict()
|
||||||
{
|
{
|
||||||
var keys = new List<KeyValue>
|
var keys = new List<KeyValue>
|
||||||
{
|
{
|
||||||
|
@ -182,17 +233,13 @@ namespace libhac
|
||||||
new KeyValue("header_key_source", 0x20, set => set.header_key_source),
|
new KeyValue("header_key_source", 0x20, set => set.header_key_source),
|
||||||
new KeyValue("header_key", 0x20, set => set.header_key),
|
new KeyValue("header_key", 0x20, set => set.header_key),
|
||||||
new KeyValue("xci_header_key", 0x10, set => set.xci_header_key),
|
new KeyValue("xci_header_key", 0x10, set => set.xci_header_key),
|
||||||
new KeyValue("encrypted_header_key", 0x20, set => set.header_key_source),
|
//new KeyValue("encrypted_header_key", 0x20, set => set.header_key_source),
|
||||||
new KeyValue("package2_key_source", 0x10, set => set.package2_key_source),
|
new KeyValue("package2_key_source", 0x10, set => set.package2_key_source),
|
||||||
new KeyValue("sd_card_kek_source", 0x10, set => set.sd_card_kek_source),
|
new KeyValue("sd_card_kek_source", 0x10, set => set.sd_card_kek_source),
|
||||||
new KeyValue("sd_card_nca_key_source", 0x20, set => set.sd_card_key_sources[1]),
|
new KeyValue("sd_card_nca_key_source", 0x20, set => set.sd_card_key_sources[1]),
|
||||||
new KeyValue("sd_card_save_key_source", 0x20, set => set.sd_card_key_sources[0]),
|
new KeyValue("sd_card_save_key_source", 0x20, set => set.sd_card_key_sources[0]),
|
||||||
new KeyValue("master_key_source", 0x10, set => set.master_key_source),
|
new KeyValue("master_key_source", 0x10, set => set.master_key_source),
|
||||||
new KeyValue("keyblob_mac_key_source", 0x10, set => set.keyblob_mac_key_source),
|
new KeyValue("keyblob_mac_key_source", 0x10, set => set.keyblob_mac_key_source),
|
||||||
new KeyValue("secure_boot_key", 0x10, set => set.secure_boot_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),
|
|
||||||
new KeyValue("eticket_rsa_kek", 0x10, set => set.eticket_rsa_kek )
|
new KeyValue("eticket_rsa_kek", 0x10, set => set.eticket_rsa_kek )
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -213,6 +260,19 @@ namespace libhac
|
||||||
keys.Add(new KeyValue($"key_area_key_system_{i:x2}", 0x10, set => set.key_area_keys[i][2]));
|
keys.Add(new KeyValue($"key_area_key_system_{i:x2}", 0x10, set => set.key_area_keys[i][2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return keys.ToDictionary(k => k.Name, k => k);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, KeyValue> CreateUniqueKeyDict()
|
||||||
|
{
|
||||||
|
var keys = new List<KeyValue>
|
||||||
|
{
|
||||||
|
new KeyValue("secure_boot_key", 0x10, set => set.secure_boot_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 < 4; slot++)
|
for (int slot = 0; slot < 4; slot++)
|
||||||
{
|
{
|
||||||
int i = slot;
|
int i = slot;
|
||||||
|
|
Loading…
Reference in a new issue