From 496250f0943d44f73c52994df8cb1f0ba8c89962 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Fri, 27 Dec 2019 15:11:36 -0700 Subject: [PATCH] hactoolnet: add nax0 support --- src/LibHac/FsSystem/AesXtsFile.cs | 2 +- src/hactoolnet/CliParser.cs | 4 ++ src/hactoolnet/ProcessNax0.cs | 81 +++++++++++++++++++++++++++++++ src/hactoolnet/Program.cs | 1 + 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/hactoolnet/ProcessNax0.cs diff --git a/src/LibHac/FsSystem/AesXtsFile.cs b/src/LibHac/FsSystem/AesXtsFile.cs index e4308efb..b143e8cb 100644 --- a/src/LibHac/FsSystem/AesXtsFile.cs +++ b/src/LibHac/FsSystem/AesXtsFile.cs @@ -12,7 +12,7 @@ namespace LibHac.FsSystem private int BlockSize { get; } private OpenMode Mode { get; } - private AesXtsFileHeader Header { get; } + public AesXtsFileHeader Header { get; } private IStorage BaseStorage { get; } internal const int HeaderLength = 0x4000; diff --git a/src/hactoolnet/CliParser.cs b/src/hactoolnet/CliParser.cs index 0dc6b6f9..386aa032 100644 --- a/src/hactoolnet/CliParser.cs +++ b/src/hactoolnet/CliParser.cs @@ -272,6 +272,10 @@ namespace hactoolnet sb.AppendLine(" --listfiles List files in save file."); sb.AppendLine(" --repack Replaces the contents of the save data with the specified directory."); sb.AppendLine(" --replacefile Replaces a file in the save data"); + sb.AppendLine("NAX0 options:"); + sb.AppendLine(" --sdseed Set console unique seed for SD card NAX0 encryption."); + sb.AppendLine(" --sdpath Set relative path for NAX0 key derivation (ex: /registered/000000FF/cafebabecafebabecafebabecafebabe.nca)."); + sb.AppendLine(" --plaintext Specify file path to save decrypted contents."); sb.AppendLine("NDV0 (Delta) options:"); sb.AppendLine(" Input delta patch can be a delta NCA file or a delta fragment file."); sb.AppendLine(" --basefile Specify base file path."); diff --git a/src/hactoolnet/ProcessNax0.cs b/src/hactoolnet/ProcessNax0.cs new file mode 100644 index 00000000..61cd737c --- /dev/null +++ b/src/hactoolnet/ProcessNax0.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using System.Text; +using LibHac; +using LibHac.Common; +using LibHac.Fs; +using LibHac.FsSystem; +using static hactoolnet.Print; + +namespace hactoolnet +{ + internal static class ProcessNax0 + { + public static void Process(Context ctx) + { + byte[][] keys = ctx.Keyset.SdCardKeys; + + using var baseFile = new LocalFile(ctx.Options.InFile, OpenMode.Read); + + AesXtsFile xtsFile = null; + int contentType = 0; + + for (int i = 0; i < keys.Length; i++) + { + byte[] kekSource = keys[i].AsSpan(0, 0x10).ToArray(); + byte[] validationKey = keys[i].AsSpan(0x10, 0x10).ToArray(); + + try + { + xtsFile = new AesXtsFile(OpenMode.Read, baseFile, ctx.Options.SdPath, kekSource, validationKey, 0x4000); + contentType = i; + + break; + } + catch (HorizonResultException) { } + } + + if (xtsFile == null) + { + ctx.Logger.LogMessage($"Error: NAX0 key derivation failed. Check SD card seed and relative path."); + return; + } + + ctx.Logger.LogMessage(xtsFile.Print(contentType)); + + if (string.IsNullOrWhiteSpace(ctx.Options.PlaintextOut)) return; + + xtsFile.AsStorage().WriteAllBytes(ctx.Options.PlaintextOut, ctx.Logger); + ctx.Logger.LogMessage($"Saved Decrypted NAX0 Content to {ctx.Options.PlaintextOut}..."); + } + + private static string Print(this AesXtsFile xtsFile, int contentType) + { + int colLen = 36; + var sb = new StringBuilder(); + sb.AppendLine(); + + sb.AppendLine("NAX0:"); + + AesXtsFileHeader header = xtsFile.Header; + uint magic = header.Magic; + + PrintItem(sb, colLen, " Magic:", Util.GetUtf8String(SpanHelpers.AsReadOnlyByteSpan(ref magic))); + PrintItem(sb, colLen, " Content Type:", GetContentType(contentType)); + PrintItem(sb, colLen, " Content Size:", $"{header.Size:x12}"); + PrintItem(sb, colLen, " Header HMAC:", header.Signature); + PrintItem(sb, colLen, " Encrypted Keys:", header.EncryptedKey1.Concat(header.EncryptedKey2).ToArray()); + PrintItem(sb, colLen, " Decrypted Keys:", header.DecryptedKey1.Concat(header.DecryptedKey2).ToArray()); + + return sb.ToString(); + } + + private static string GetContentType(int type) => type switch + { + 0 => "Save", + 1 => "NCA", + 2 => "Custom storage", + _ => "Unknown" + }; + } +} diff --git a/src/hactoolnet/Program.cs b/src/hactoolnet/Program.cs index 7636229e..3d0514d7 100644 --- a/src/hactoolnet/Program.cs +++ b/src/hactoolnet/Program.cs @@ -126,6 +126,7 @@ namespace hactoolnet ProcessFsBuild.ProcessRomFs(ctx); break; case FileType.Nax0: + ProcessNax0.Process(ctx); break; case FileType.SwitchFs: ProcessSwitchFs.Process(ctx);