mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Allow user to supply a titlekey on the command-line when processing NCAs (#290)
* feat: Enable user to supply a single titlekey when processing NCAs * chore: update README * feat: add `basetitlekey` support * chore: README * fix: check hasnt already been added via title.keys * fix: Update logic so commandline supplied keys override existing keys in file * hactoolnet: Parse title keys when reading CLI arguments
This commit is contained in:
parent
dd478db70a
commit
300fa877e4
4 changed files with 59 additions and 1 deletions
|
@ -55,6 +55,8 @@ NCA options:
|
||||||
--romfsdir <dir> Specify RomFS directory path.
|
--romfsdir <dir> Specify RomFS directory path.
|
||||||
--listromfs List files in RomFS.
|
--listromfs List files in RomFS.
|
||||||
--basenca Set Base NCA to use with update partitions.
|
--basenca Set Base NCA to use with update partitions.
|
||||||
|
--basetitlekey Specify single (encrypted) titlekey for the base NCA.
|
||||||
|
--titlekey Specify single (encrypted) titlekey.
|
||||||
KIP1 options:
|
KIP1 options:
|
||||||
--uncompressed <f> Specify file path for saving uncompressed KIP1.
|
--uncompressed <f> Specify file path for saving uncompressed KIP1.
|
||||||
RomFS options:
|
RomFS options:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace hactoolnet;
|
namespace hactoolnet;
|
||||||
|
|
||||||
|
@ -49,6 +50,8 @@ internal static class CliParser
|
||||||
new CliOption("sdseed", 1, (o, a) => o.SdSeed = a[0]),
|
new CliOption("sdseed", 1, (o, a) => o.SdSeed = a[0]),
|
||||||
new CliOption("sdpath", 1, (o, a) => o.SdPath = a[0]),
|
new CliOption("sdpath", 1, (o, a) => o.SdPath = a[0]),
|
||||||
new CliOption("basenca", 1, (o, a) => o.BaseNca = a[0]),
|
new CliOption("basenca", 1, (o, a) => o.BaseNca = a[0]),
|
||||||
|
new CliOption("basetitlekey", 1, (o, a) => o.BaseTitleKey = ParseTitleKey(o, a[0])),
|
||||||
|
new CliOption("titlekey", 1, (o, a) => o.TitleKey = ParseTitleKey(o, a[0])),
|
||||||
new CliOption("basefile", 1, (o, a) => o.BaseFile = a[0]),
|
new CliOption("basefile", 1, (o, a) => o.BaseFile = a[0]),
|
||||||
new CliOption("rootdir", 1, (o, a) => o.RootDir = a[0]),
|
new CliOption("rootdir", 1, (o, a) => o.RootDir = a[0]),
|
||||||
new CliOption("updatedir", 1, (o, a) => o.UpdateDir = a[0]),
|
new CliOption("updatedir", 1, (o, a) => o.UpdateDir = a[0]),
|
||||||
|
@ -214,6 +217,19 @@ internal static class CliParser
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static byte[] ParseTitleKey(Options options, string input)
|
||||||
|
{
|
||||||
|
byte[] key = new byte[32];
|
||||||
|
|
||||||
|
if (input.Length != 32 || !StringUtils.TryFromHexString(input, key))
|
||||||
|
{
|
||||||
|
options.ParseErrorMessage ??= "TitleKey must be 32 hex characters long";
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
private static double ParseDouble(Options options, string input)
|
private static double ParseDouble(Options options, string input)
|
||||||
{
|
{
|
||||||
if (!double.TryParse(input, out double value))
|
if (!double.TryParse(input, out double value))
|
||||||
|
@ -276,6 +292,8 @@ internal static class CliParser
|
||||||
sb.AppendLine(" --romfsdir <dir> Specify RomFS directory path.");
|
sb.AppendLine(" --romfsdir <dir> Specify RomFS directory path.");
|
||||||
sb.AppendLine(" --listromfs List files in RomFS.");
|
sb.AppendLine(" --listromfs List files in RomFS.");
|
||||||
sb.AppendLine(" --basenca Set Base NCA to use with update partitions.");
|
sb.AppendLine(" --basenca Set Base NCA to use with update partitions.");
|
||||||
|
sb.AppendLine(" --basetitlekey Specify single (encrypted) titlekey for the base NCA.");
|
||||||
|
sb.AppendLine(" --titlekey Specify single (encrypted) titlekey for the NCA.");
|
||||||
sb.AppendLine("KIP1 options:");
|
sb.AppendLine("KIP1 options:");
|
||||||
sb.AppendLine(" --uncompressed <f> Specify file path for saving uncompressed KIP1.");
|
sb.AppendLine(" --uncompressed <f> Specify file path for saving uncompressed KIP1.");
|
||||||
sb.AppendLine("RomFS options:");
|
sb.AppendLine("RomFS options:");
|
||||||
|
|
|
@ -62,6 +62,8 @@ internal class Options
|
||||||
public bool BuildHfs;
|
public bool BuildHfs;
|
||||||
public bool ExtractIni1;
|
public bool ExtractIni1;
|
||||||
public ulong TitleId;
|
public ulong TitleId;
|
||||||
|
public byte[] TitleKey;
|
||||||
|
public byte[] BaseTitleKey;
|
||||||
public string BenchType;
|
public string BenchType;
|
||||||
public double CpuFrequencyGhz;
|
public double CpuFrequencyGhz;
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Common.Keys;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.Fs.Impl;
|
using LibHac.Fs.Impl;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Spl;
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using LibHac.Tools.Npdm;
|
using LibHac.Tools.Npdm;
|
||||||
|
using LibHac.Util;
|
||||||
using static hactoolnet.Print;
|
using static hactoolnet.Print;
|
||||||
using NcaFsHeader = LibHac.Tools.FsSystem.NcaUtils.NcaFsHeader;
|
using NcaFsHeader = LibHac.Tools.FsSystem.NcaUtils.NcaFsHeader;
|
||||||
|
|
||||||
|
@ -24,6 +28,15 @@ internal static class ProcessNca
|
||||||
var nca = new Nca(ctx.KeySet, file);
|
var nca = new Nca(ctx.KeySet, file);
|
||||||
Nca baseNca = null;
|
Nca baseNca = null;
|
||||||
|
|
||||||
|
if (ctx.Options.TitleKey != null && nca.Header.HasRightsId)
|
||||||
|
{
|
||||||
|
if (!TryAddTitleKey(ctx.KeySet, ctx.Options.TitleKey, nca.Header.RightsId))
|
||||||
|
{
|
||||||
|
ctx.Logger.LogMessage($"Invalid title key \"{ctx.Options.TitleKey}\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var ncaHolder = new NcaHolder { Nca = nca };
|
var ncaHolder = new NcaHolder { Nca = nca };
|
||||||
|
|
||||||
if (ctx.Options.HeaderOut != null)
|
if (ctx.Options.HeaderOut != null)
|
||||||
|
@ -38,6 +51,15 @@ internal static class ProcessNca
|
||||||
{
|
{
|
||||||
IStorage baseFile = new LocalStorage(ctx.Options.BaseNca, FileAccess.Read);
|
IStorage baseFile = new LocalStorage(ctx.Options.BaseNca, FileAccess.Read);
|
||||||
baseNca = new Nca(ctx.KeySet, baseFile);
|
baseNca = new Nca(ctx.KeySet, baseFile);
|
||||||
|
|
||||||
|
if (ctx.Options.BaseTitleKey != null && baseNca.Header.HasRightsId)
|
||||||
|
{
|
||||||
|
if (!TryAddTitleKey(ctx.KeySet, ctx.Options.BaseTitleKey, baseNca.Header.RightsId))
|
||||||
|
{
|
||||||
|
ctx.Logger.LogMessage($"Invalid base title key \"{ctx.Options.BaseTitleKey}\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
|
@ -252,6 +274,20 @@ internal static class ProcessNca
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryAddTitleKey(KeySet keySet, ReadOnlySpan<byte> key, ReadOnlySpan<byte> rightsId)
|
||||||
|
{
|
||||||
|
if (key.Length != 32)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var titleKey = new AccessKey(key);
|
||||||
|
var rId = new RightsId(rightsId);
|
||||||
|
|
||||||
|
keySet.ExternalKeySet.Remove(rId);
|
||||||
|
keySet.ExternalKeySet.Add(rId, titleKey);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static Validity VerifySignature2(this Nca nca)
|
private static Validity VerifySignature2(this Nca nca)
|
||||||
{
|
{
|
||||||
if (nca.Header.ContentType != NcaContentType.Program) return Validity.Unchecked;
|
if (nca.Header.ContentType != NcaContentType.Program) return Validity.Unchecked;
|
||||||
|
|
Loading…
Reference in a new issue