hactoolnet: Always read both prod and dev key sets

This commit is contained in:
Alex Barney 2020-10-11 22:24:53 -07:00
parent 8f011387a0
commit c39895080b
3 changed files with 117 additions and 32 deletions

View file

@ -65,6 +65,61 @@ namespace LibHac.Common.Keys
keySet.DeriveKeys(logger);
// Dev keys can be read from prod key files, so derive any missing keys if necessary.
if (keySet.CurrentMode == KeySet.Mode.Prod)
{
keySet.SetMode(KeySet.Mode.Dev);
keySet.DeriveKeys(logger);
keySet.SetMode(KeySet.Mode.Prod);
}
}
/// <summary>
/// Loads keys from key files into an existing <see cref="KeySet"/>. Missing keys will be
/// derived from existing keys if possible. Any <see langword="null"/> file names will be skipped.
/// </summary>
/// <param name="keySet">The <see cref="KeySet"/> where the loaded keys will be placed.</param>
/// <param name="prodKeysFilename">The path of the file containing common prod keys. Can be <see langword="null"/>.</param>
/// <param name="devKeysFilename">The path of the file containing common dev keys. Can be <see langword="null"/>.</param>
/// <param name="titleKeysFilename">The path of the file containing title keys. Can be <see langword="null"/>.</param>
/// <param name="consoleKeysFilename">The path of the file containing device-unique keys. Can be <see langword="null"/>.</param>
/// <param name="logger">An optional logger that key-parsing errors will be written to.</param>
public static void ReadKeyFile(KeySet keySet, string prodKeysFilename = null, string devKeysFilename = null,
string titleKeysFilename = null, string consoleKeysFilename = null, IProgressReport logger = null)
{
KeySet.Mode originalMode = keySet.CurrentMode;
List<KeyInfo> keyInfos = DefaultKeySet.CreateKeyList();
if (prodKeysFilename != null)
{
keySet.SetMode(KeySet.Mode.Prod);
using var storage = new FileStream(prodKeysFilename, FileMode.Open, FileAccess.Read);
ReadMainKeys(keySet, storage, keyInfos, logger);
}
if (devKeysFilename != null)
{
keySet.SetMode(KeySet.Mode.Dev);
using var storage = new FileStream(devKeysFilename, FileMode.Open, FileAccess.Read);
ReadMainKeys(keySet, storage, keyInfos, logger);
}
keySet.SetMode(originalMode);
if (consoleKeysFilename != null)
{
using var storage = new FileStream(consoleKeysFilename, FileMode.Open, FileAccess.Read);
ReadMainKeys(keySet, storage, keyInfos, logger);
}
if (titleKeysFilename != null)
{
using var storage = new FileStream(titleKeysFilename, FileMode.Open, FileAccess.Read);
ReadTitleKeys(keySet, storage, logger);
}
keySet.DeriveKeys(logger);
// Dev keys can read from prod key files, so derive any missing keys if necessary.
if (keySet.CurrentMode == KeySet.Mode.Prod)
{
@ -283,7 +338,12 @@ namespace LibHac.Common.Keys
return ReaderStatus.Finished;
}
buffer = buffer.Slice(0, buffer.Length - reader.BufferPos + charsRead);
// ReadBlock will only read less than the buffer size if there's nothing left to read
if (charsRead != reader.BufferPos)
{
buffer = buffer.Slice(0, buffer.Length - reader.BufferPos + charsRead);
reader.Buffer = buffer;
}
reader.NeedFillBuffer = false;
reader.BufferPos = 0;

View file

@ -13,7 +13,6 @@ 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;
@ -156,18 +155,18 @@ namespace LibHac.Common.Keys
public static string PrintCommonKeysWithDev(KeySet keySet)
{
KeySet.Mode originalMode = keySet.CurrentMode;
var sb = new StringBuilder();
keySet.SetMode(KeySet.Mode.Prod);
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);
}
sb.AppendLine();
keySet.SetMode(KeySet.Mode.Dev);
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Derived, true);
keySet.SetMode(originalMode);
return sb.ToString();
}
}

View file

@ -85,7 +85,7 @@ namespace hactoolnet
Result.SetLogger(resultLogger);
}
OpenKeyset(ctx);
OpenKeySet(ctx);
if (ctx.Options.RunCustom)
{
@ -168,43 +168,65 @@ namespace hactoolnet
}
}
private static void OpenKeyset(Context ctx)
private static void OpenKeySet(Context ctx)
{
string keyFileName = ctx.Options.UseDevKeys ? "dev.keys" : "prod.keys";
#if CORERT_NO_REFLECTION
string home = HomeFolder.GetFolderPath(Environment.SpecialFolder.UserProfile);
#else
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
#endif
string homeKeyFile = Path.Combine(home, ".switch", keyFileName);
string homeTitleKeyFile = Path.Combine(home, ".switch", "title.keys");
string homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys");
string keyFile = ctx.Options.Keyfile;
string prodKeyFile = Path.Combine(home, ".switch", "prod.keys");
string devKeyFile = Path.Combine(home, ".switch", "dev.keys");
string titleKeyFile = ctx.Options.TitleKeyFile;
string consoleKeyFile = ctx.Options.ConsoleKeyFile;
if (keyFile == null && File.Exists(homeKeyFile))
{
keyFile = homeKeyFile;
}
// Check if the files from the command line exist
if (titleKeyFile != null && !File.Exists(titleKeyFile))
titleKeyFile = null;
if (consoleKeyFile != null && !File.Exists(consoleKeyFile))
consoleKeyFile = null;
if (!File.Exists(prodKeyFile))
prodKeyFile = null;
if (!File.Exists(devKeyFile))
devKeyFile = null;
// Check the home directory if no existing key files were specified
if (consoleKeyFile == null && File.Exists(homeConsoleKeyFile))
consoleKeyFile = homeConsoleKeyFile;
if (titleKeyFile == null && File.Exists(homeTitleKeyFile))
{
titleKeyFile = homeTitleKeyFile;
}
if (consoleKeyFile == null && File.Exists(homeConsoleKeyFile))
var keySet = KeySet.CreateDefaultKeySet();
// If the user specifies a key file then only load that file into the mode they specified,
// otherwise load both prod.keys and dev.keys.
// Todo: Should we add a way that both dev-only key files and mixed prod/dev key files
// can both be loaded when specifying a key file in dev mode?
if (ctx.Options.Keyfile != null && File.Exists(ctx.Options.Keyfile))
{
consoleKeyFile = homeConsoleKeyFile;
keySet.SetMode(ctx.Options.KeyMode);
ExternalKeyReader.ReadKeyFile(keySet, ctx.Options.Keyfile, titleKeyFile, consoleKeyFile, ctx.Logger);
}
else
{
ExternalKeyReader.ReadKeyFile(keySet, prodKeyFile, devKeyFile, titleKeyFile, consoleKeyFile, ctx.Logger);
}
ctx.KeySet = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile, ctx.Logger, ctx.Options.KeyMode);
keySet.SetMode(ctx.Options.KeyMode);
if (ctx.Options.SdSeed != null)
{
ctx.KeySet.SetSdSeed(ctx.Options.SdSeed.ToBytes());
keySet.SetSdSeed(ctx.Options.SdSeed.ToBytes());
}
ctx.KeySet = keySet;
}
private static void ProcessKeygen(Context ctx)
@ -213,19 +235,23 @@ namespace hactoolnet
if (ctx.Options.OutDir != null)
{
string keyFileName = ctx.Options.UseDevKeys ? "dev.keys" : "prod.keys";
KeySet.Mode originalMode = ctx.KeySet.CurrentMode;
string dir = ctx.Options.OutDir;
Directory.CreateDirectory(dir);
File.WriteAllText(Path.Combine(dir, keyFileName), ExternalKeyWriter.PrintCommonKeys(ctx.KeySet));
ctx.KeySet.SetMode(KeySet.Mode.Prod);
File.WriteAllText(Path.Combine(dir, "prod.keys"), ExternalKeyWriter.PrintCommonKeys(ctx.KeySet));
ctx.KeySet.SetMode(KeySet.Mode.Dev);
File.WriteAllText(Path.Combine(dir, "dev.keys"), ExternalKeyWriter.PrintCommonKeys(ctx.KeySet));
ctx.KeySet.SetMode(originalMode);
File.WriteAllText(Path.Combine(dir, "console.keys"), ExternalKeyWriter.PrintDeviceKeys(ctx.KeySet));
File.WriteAllText(Path.Combine(dir, "title.keys"), ExternalKeyWriter.PrintTitleKeys(ctx.KeySet));
if (!ctx.Options.UseDevKeys)
{
File.WriteAllText(Path.Combine(dir, "prod+dev.keys"),
ExternalKeyWriter.PrintCommonKeysWithDev(ctx.KeySet));
}
File.WriteAllText(Path.Combine(dir, "prod+dev.keys"),
ExternalKeyWriter.PrintCommonKeysWithDev(ctx.KeySet));
}
}