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;
}
if (!inputSpecified)
if (!inputSpecified && options.InFileType != FileType.Keygen)
{
PrintWithUsage("Input file must be specified");
return null;
@ -145,7 +145,7 @@ namespace hactoolnet
sb.AppendLine(" -r, --raw Keep raw data, don\'t unpack.");
sb.AppendLine(" -y, --verify Verify hashes.");
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("NCA options:");
sb.AppendLine(" --section0 <file> Specify Section 0 file path.");

View file

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

View file

@ -48,6 +48,9 @@ namespace hactoolnet
case FileType.Xci:
ProcessXci(ctx);
break;
case FileType.Keygen:
ProcessKeygen(ctx);
break;
default:
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
// ReSharper disable once UnusedParameter.Local
private static void CustomTask(Context ctx)

View file

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace libhac
{
@ -59,13 +60,45 @@ namespace libhac
internal void DeriveKeys()
{
//var cmac = new byte[0x10];
//for (int i = 0; i < 0x20; i++)
//{
// Crypto.DecryptEcb(tsec_key, keyblob_key_sources[i], keyblob_keys[i], 0x10);
// Crypto.DecryptEcb(secure_boot_key, keyblob_keys[i], keyblob_keys[i], 0x10);
// Crypto.DecryptEcb(keyblob_keys[i], keyblob_mac_key_source, keyblob_mac_keys[i], 0x10);
//}
for (int i = 0; i < 0x20; i++)
{
if (master_keys[i].IsEmpty())
{
continue;
}
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];
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
{
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)
{
var keyset = new Keyset();
if (filename != null) ReadMainKeys(keyset, filename, progress);
if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, progress);
if (filename != null) ReadMainKeys(keyset, filename, CommonKeyDict, progress);
if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, UniqueKeyDict, progress);
if (titleKeysFilename != null) ReadTitleKeys(keyset, titleKeysFilename, progress);
keyset.DeriveKeys();
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;
@ -117,16 +151,16 @@ namespace libhac
var key = a[0].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;
}
var value = valueStr.ToBytes();
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;
}
@ -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>
{
@ -182,17 +233,13 @@ namespace libhac
new KeyValue("header_key_source", 0x20, set => set.header_key_source),
new KeyValue("header_key", 0x20, set => set.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("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_save_key_source", 0x20, set => set.sd_card_key_sources[0]),
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("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 )
};
@ -213,6 +260,19 @@ namespace libhac
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++)
{
int i = slot;