Generate keys from master key + source

This commit is contained in:
Alex Barney 2018-08-27 17:40:06 -05:00
parent 0bea29e59e
commit e7f5702477
4 changed files with 92 additions and 23 deletions

View file

@ -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.");

View file

@ -43,7 +43,8 @@ namespace hactoolnet
Nax0, Nax0,
Xci, Xci,
SwitchFs, SwitchFs,
Save Save,
Keygen
} }
internal class Context internal class Context

View file

@ -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)

View file

@ -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;