From c39895080be078e5330c713b9c6fb055b4c7d14a Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 11 Oct 2020 22:24:53 -0700 Subject: [PATCH] hactoolnet: Always read both prod and dev key sets --- src/LibHac/Common/Keys/ExternalKeyReader.cs | 62 +++++++++++++++++- src/LibHac/Common/Keys/ExternalKeyWriter.cs | 15 ++--- src/hactoolnet/Program.cs | 72 ++++++++++++++------- 3 files changed, 117 insertions(+), 32 deletions(-) diff --git a/src/LibHac/Common/Keys/ExternalKeyReader.cs b/src/LibHac/Common/Keys/ExternalKeyReader.cs index 8c530728..5267726d 100644 --- a/src/LibHac/Common/Keys/ExternalKeyReader.cs +++ b/src/LibHac/Common/Keys/ExternalKeyReader.cs @@ -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); + } + } + + /// + /// Loads keys from key files into an existing . Missing keys will be + /// derived from existing keys if possible. Any file names will be skipped. + /// + /// The where the loaded keys will be placed. + /// The path of the file containing common prod keys. Can be . + /// The path of the file containing common dev keys. Can be . + /// The path of the file containing title keys. Can be . + /// The path of the file containing device-unique keys. Can be . + /// An optional logger that key-parsing errors will be written to. + 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 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; diff --git a/src/LibHac/Common/Keys/ExternalKeyWriter.cs b/src/LibHac/Common/Keys/ExternalKeyWriter.cs index 3faa290d..c284935e 100644 --- a/src/LibHac/Common/Keys/ExternalKeyWriter.cs +++ b/src/LibHac/Common/Keys/ExternalKeyWriter.cs @@ -13,7 +13,6 @@ namespace LibHac.Common.Keys { public static class ExternalKeyWriter { - public static void PrintKeys(KeySet keySet, StringBuilder sb, List 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(); } } diff --git a/src/hactoolnet/Program.cs b/src/hactoolnet/Program.cs index 2b0dfe6c..1a3dcbd2 100644 --- a/src/hactoolnet/Program.cs +++ b/src/hactoolnet/Program.cs @@ -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)); } }