From 32996aed3cbb50a4aabd9245f0620966691fc45f Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 25 Jun 2018 14:01:24 -0500 Subject: [PATCH] Have Nax0 dispose stream --- hactoolnet/Program.cs | 35 ++++++++---- libhac/Cnmt.cs | 4 +- libhac/Nax0.cs | 125 +++++++++++++++++++++++++----------------- libhac/NcaStructs.cs | 1 + libhac/SdFs.cs | 2 +- 5 files changed, 103 insertions(+), 64 deletions(-) diff --git a/hactoolnet/Program.cs b/hactoolnet/Program.cs index f9072f9e..34c60043 100644 --- a/hactoolnet/Program.cs +++ b/hactoolnet/Program.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using libhac; @@ -10,7 +11,15 @@ namespace hactoolnet { static void Main(string[] args) { + Console.WriteLine("Listing NCA files"); + ListSdContents(args); + + Console.WriteLine("Listing titles"); + var watch = Stopwatch.StartNew(); + DumpMeta(args); + watch.Stop(); + Console.WriteLine(watch.Elapsed.TotalMilliseconds); } static void DecryptNax0(string[] args) @@ -18,15 +27,17 @@ namespace hactoolnet var keyset = ExternalKeys.ReadKeyFile(args[0]); keyset.SetSdSeed(args[1].ToBytes()); - var nax0 = new Nax0(keyset, args[2], args[3]); - var nca = new Nca(keyset, nax0.Stream); - - using (var output = new FileStream(args[4], FileMode.Create)) - using (var progress = new ProgressBar()) + using (var nax0 = Nax0.CreateFromPath(keyset, args[2], args[3])) { - progress.LogMessage($"Title ID: {nca.Header.TitleId:X16}"); - progress.LogMessage($"Writing {args[4]}"); - nax0.Stream.CopyStream(output, nax0.Stream.Length, progress); + var nca = new Nca(keyset, nax0.Stream); + + using (var output = new FileStream(args[4], FileMode.Create)) + using (var progress = new ProgressBar()) + { + progress.LogMessage($"Title ID: {nca.Header.TitleId:X16}"); + progress.LogMessage($"Writing {args[4]}"); + nax0.Stream.CopyStream(output, nax0.Stream.Length, progress); + } } } @@ -46,7 +57,7 @@ namespace hactoolnet var sdfs = new SdFs(keyset, args[2]); var ncas = sdfs.ReadAllNca(); - foreach (var nca in ncas.Where(x => x != null)) + foreach (Nca nca in ncas) { Console.WriteLine($"{nca.Header.TitleId:X16} {nca.Header.ContentType.ToString().PadRight(10, ' ')} {nca.Name}"); } @@ -85,14 +96,14 @@ namespace hactoolnet foreach (var meta in metadata.OrderBy(x => x.TitleId)) { - progress.LogMessage($"{meta.TitleId:X16} v{meta.TitleVersion} {meta.Type}"); + // progress.LogMessage($"{meta.TitleId:X16} v{meta.TitleVersion} {meta.Type}"); foreach (var content in meta.ContentEntries) { // Add an actual hexdump function - progress.LogMessage($" {BitConverter.ToString(content.NcaId).Replace("-", "")}.nca {content.Type}"); + // progress.LogMessage($" {BitConverter.ToString(content.NcaId).Replace("-", "")}.nca {content.Type}"); } - progress.LogMessage(""); + //progress.LogMessage(""); } } } diff --git a/libhac/Cnmt.cs b/libhac/Cnmt.cs index 050a778b..ed7d8787 100644 --- a/libhac/Cnmt.cs +++ b/libhac/Cnmt.cs @@ -74,8 +74,8 @@ namespace libhac SystemUpdate, FirmwarePackageA, FirmwarePackageB, - RegularApplication = 0x80, - UpdateTitle, + Application = 0x80, + Patch, AddOnContent, DeltaTitle } diff --git a/libhac/Nax0.cs b/libhac/Nax0.cs index bf097c2d..f3115b0b 100644 --- a/libhac/Nax0.cs +++ b/libhac/Nax0.cs @@ -7,62 +7,64 @@ using libhac.XTSSharp; namespace libhac { - public class Nax0 + public class Nax0 : IDisposable { - public List Files = new List(); - public byte[] Hmac { get; set; } + public byte[] Hmac { get; private set; } public byte[][] EncKeys { get; } = Util.CreateJaggedArray(2, 0x10); public byte[][] Keys { get; } = Util.CreateJaggedArray(2, 0x10); - public long Length { get; set; } + public long Length { get; private set; } public Stream Stream { get; } - private List Streams = new List(); + private bool KeepOpen { get; } - public Nax0(Keyset keyset, string path, string sdPath) + public Nax0(Keyset keyset, Stream stream, string sdPath, bool keepOpen) { - if (Directory.Exists(path)) - { - while (true) - { - var partName = Path.Combine(path, $"{Files.Count:D2}"); - if (!File.Exists(partName)) break; - - Files.Add(partName); - } - - } - else if (File.Exists(path)) - { - Files.Add(path); - } - else - { - throw new FileNotFoundException("Could not find the input file or directory"); - } - - foreach (var file in Files) - { - Streams.Add(new FileStream(file, FileMode.Open)); - } - - var stream = new CombinationStream(Streams); + stream.Position = 0; + KeepOpen = keepOpen; ReadHeader(stream); + DeriveKeys(keyset, sdPath); + ValidateKeys(keyset, stream); + stream.Position = 0x4000; + var xts = XtsAes128.Create(Keys[0], Keys[1]); + Stream = new RandomAccessSectorStream(new XtsSectorStream(stream, xts, 0x4000, 0x4000)); + } + + private void ReadHeader(Stream stream) + { + var header = new byte[0x60]; + stream.Read(header, 0, 0x60); + var reader = new BinaryReader(new MemoryStream(header)); + + Hmac = reader.ReadBytes(0x20); + string magic = reader.ReadAscii(4); + reader.BaseStream.Position += 4; + if (magic != "NAX0") throw new InvalidDataException("Not an NAX0 file"); + EncKeys[0] = reader.ReadBytes(0x10); + EncKeys[1] = reader.ReadBytes(0x10); + Length = reader.ReadInt64(); + } + + private void DeriveKeys(Keyset keyset, string sdPath) + { for (int k = 0; k < 2; k++) { var naxSpecificKeys = Util.CreateJaggedArray(2, 0x10); - var hashKey2 = new byte[0x10]; - Array.Copy(keyset.sd_card_keys[k], hashKey2, 0x10); + var hashKey = new byte[0x10]; + Array.Copy(keyset.sd_card_keys[k], hashKey, 0x10); - var hash2 = new HMACSHA256(hashKey2); + var hash = new HMACSHA256(hashKey); var sdPathBytes = Encoding.ASCII.GetBytes(sdPath); - var checksum = hash2.ComputeHash(sdPathBytes, 0, sdPathBytes.Length); + var checksum = hash.ComputeHash(sdPathBytes, 0, sdPathBytes.Length); Array.Copy(checksum, 0, naxSpecificKeys[0], 0, 0x10); Array.Copy(checksum, 0x10, naxSpecificKeys[1], 0, 0x10); Crypto.DecryptEcb(naxSpecificKeys[0], EncKeys[0], Keys[0], 0x10); Crypto.DecryptEcb(naxSpecificKeys[1], EncKeys[1], Keys[1], 0x10); } + } + private void ValidateKeys(Keyset keyset, Stream stream) + { stream.Position = 0x20; var hashKey = new byte[0x60]; stream.Read(hashKey, 0, 0x60); @@ -74,23 +76,48 @@ namespace libhac var isValid = Util.ArraysEqual(Hmac, validationMac); if (!isValid) throw new ArgumentException("NAX0 key derivation failed."); - - stream.Position = 0x4000; - - var xts = XtsAes128.Create(Keys[0], Keys[1]); - Stream = new RandomAccessSectorStream(new XtsSectorStream(stream, xts, 0x4000, 0x4000)); } - private void ReadHeader(Stream nax0) + public static Nax0 CreateFromPath(Keyset keyset, string path, string sdPath) { - var reader = new BinaryReader(nax0); - nax0.Position = 0; + List files = new List(); + List streams = new List(); - Hmac = reader.ReadBytes(0x20); - nax0.Position += 8; //todo check magic - EncKeys[0] = reader.ReadBytes(0x10); - EncKeys[1] = reader.ReadBytes(0x10); - Length = reader.ReadInt64(); + if (Directory.Exists(path)) + { + while (true) + { + var partName = Path.Combine(path, $"{files.Count:D2}"); + if (!File.Exists(partName)) break; + + files.Add(partName); + } + + } + else if (File.Exists(path)) + { + files.Add(path); + } + else + { + throw new FileNotFoundException("Could not find the input file or directory"); + } + + foreach (var file in files) + { + streams.Add(new FileStream(file, FileMode.Open)); + } + + var stream = new CombinationStream(streams); + return new Nax0(keyset, stream, sdPath, false); + } + + public void Dispose() + { + if (!KeepOpen) + { + Stream?.Dispose(); + } } } } diff --git a/libhac/NcaStructs.cs b/libhac/NcaStructs.cs index 7b7b1042..7c6eb938 100644 --- a/libhac/NcaStructs.cs +++ b/libhac/NcaStructs.cs @@ -31,6 +31,7 @@ namespace libhac head.Signature1 = reader.ReadBytes(0x100); head.Signature2 = reader.ReadBytes(0x100); head.Magic = reader.ReadAscii(4); + if(head.Magic != "NCA3") throw new InvalidDataException("Not an NCA3 file"); head.Distribution = reader.ReadByte(); head.ContentType = (ContentType)reader.ReadByte(); head.CryptoType = reader.ReadByte(); diff --git a/libhac/SdFs.cs b/libhac/SdFs.cs index b318d8a5..a6689814 100644 --- a/libhac/SdFs.cs +++ b/libhac/SdFs.cs @@ -32,7 +32,7 @@ namespace libhac try { var sdPath = "/" + Util.GetRelativePath(file, ContentsDir).Replace('\\', '/'); - var nax0 = new Nax0(Keyset, file, sdPath); + var nax0 = Nax0.CreateFromPath(Keyset, file, sdPath); nca = new Nca(Keyset, nax0.Stream); nca.Name = Path.GetFileName(file); }