Have Nax0 dispose stream

This commit is contained in:
Alex Barney 2018-06-25 14:01:24 -05:00
parent 88f72a3527
commit 32996aed3c
5 changed files with 103 additions and 64 deletions

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using libhac; using libhac;
@ -10,7 +11,15 @@ namespace hactoolnet
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
Console.WriteLine("Listing NCA files");
ListSdContents(args);
Console.WriteLine("Listing titles");
var watch = Stopwatch.StartNew();
DumpMeta(args); DumpMeta(args);
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
} }
static void DecryptNax0(string[] args) static void DecryptNax0(string[] args)
@ -18,7 +27,8 @@ namespace hactoolnet
var keyset = ExternalKeys.ReadKeyFile(args[0]); var keyset = ExternalKeys.ReadKeyFile(args[0]);
keyset.SetSdSeed(args[1].ToBytes()); keyset.SetSdSeed(args[1].ToBytes());
var nax0 = new Nax0(keyset, args[2], args[3]); using (var nax0 = Nax0.CreateFromPath(keyset, args[2], args[3]))
{
var nca = new Nca(keyset, nax0.Stream); var nca = new Nca(keyset, nax0.Stream);
using (var output = new FileStream(args[4], FileMode.Create)) using (var output = new FileStream(args[4], FileMode.Create))
@ -29,6 +39,7 @@ namespace hactoolnet
nax0.Stream.CopyStream(output, nax0.Stream.Length, progress); nax0.Stream.CopyStream(output, nax0.Stream.Length, progress);
} }
} }
}
static void ListSdContents(string[] args) static void ListSdContents(string[] args)
{ {
@ -46,7 +57,7 @@ namespace hactoolnet
var sdfs = new SdFs(keyset, args[2]); var sdfs = new SdFs(keyset, args[2]);
var ncas = sdfs.ReadAllNca(); 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}"); 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)) 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) foreach (var content in meta.ContentEntries)
{ {
// Add an actual hexdump function // 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("");
} }
} }
} }

View file

@ -74,8 +74,8 @@ namespace libhac
SystemUpdate, SystemUpdate,
FirmwarePackageA, FirmwarePackageA,
FirmwarePackageB, FirmwarePackageB,
RegularApplication = 0x80, Application = 0x80,
UpdateTitle, Patch,
AddOnContent, AddOnContent,
DeltaTitle DeltaTitle
} }

View file

@ -7,62 +7,64 @@ using libhac.XTSSharp;
namespace libhac namespace libhac
{ {
public class Nax0 public class Nax0 : IDisposable
{ {
public List<string> Files = new List<string>(); public byte[] Hmac { get; private set; }
public byte[] Hmac { get; set; }
public byte[][] EncKeys { get; } = Util.CreateJaggedArray<byte[][]>(2, 0x10); public byte[][] EncKeys { get; } = Util.CreateJaggedArray<byte[][]>(2, 0x10);
public byte[][] Keys { get; } = Util.CreateJaggedArray<byte[][]>(2, 0x10); public byte[][] Keys { get; } = Util.CreateJaggedArray<byte[][]>(2, 0x10);
public long Length { get; set; } public long Length { get; private set; }
public Stream Stream { get; } public Stream Stream { get; }
private List<Stream> Streams = new List<Stream>(); 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)) stream.Position = 0;
{ KeepOpen = keepOpen;
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);
ReadHeader(stream); 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++) for (int k = 0; k < 2; k++)
{ {
var naxSpecificKeys = Util.CreateJaggedArray<byte[][]>(2, 0x10); var naxSpecificKeys = Util.CreateJaggedArray<byte[][]>(2, 0x10);
var hashKey2 = new byte[0x10]; var hashKey = new byte[0x10];
Array.Copy(keyset.sd_card_keys[k], hashKey2, 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 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, 0, naxSpecificKeys[0], 0, 0x10);
Array.Copy(checksum, 0x10, naxSpecificKeys[1], 0, 0x10); Array.Copy(checksum, 0x10, naxSpecificKeys[1], 0, 0x10);
Crypto.DecryptEcb(naxSpecificKeys[0], EncKeys[0], Keys[0], 0x10); Crypto.DecryptEcb(naxSpecificKeys[0], EncKeys[0], Keys[0], 0x10);
Crypto.DecryptEcb(naxSpecificKeys[1], EncKeys[1], Keys[1], 0x10); Crypto.DecryptEcb(naxSpecificKeys[1], EncKeys[1], Keys[1], 0x10);
} }
}
private void ValidateKeys(Keyset keyset, Stream stream)
{
stream.Position = 0x20; stream.Position = 0x20;
var hashKey = new byte[0x60]; var hashKey = new byte[0x60];
stream.Read(hashKey, 0, 0x60); stream.Read(hashKey, 0, 0x60);
@ -74,23 +76,48 @@ namespace libhac
var isValid = Util.ArraysEqual(Hmac, validationMac); var isValid = Util.ArraysEqual(Hmac, validationMac);
if (!isValid) throw new ArgumentException("NAX0 key derivation failed."); 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); List<string> files = new List<string>();
nax0.Position = 0; List<Stream> streams = new List<Stream>();
Hmac = reader.ReadBytes(0x20); if (Directory.Exists(path))
nax0.Position += 8; //todo check magic {
EncKeys[0] = reader.ReadBytes(0x10); while (true)
EncKeys[1] = reader.ReadBytes(0x10); {
Length = reader.ReadInt64(); 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();
}
} }
} }
} }

View file

@ -31,6 +31,7 @@ namespace libhac
head.Signature1 = reader.ReadBytes(0x100); head.Signature1 = reader.ReadBytes(0x100);
head.Signature2 = reader.ReadBytes(0x100); head.Signature2 = reader.ReadBytes(0x100);
head.Magic = reader.ReadAscii(4); head.Magic = reader.ReadAscii(4);
if(head.Magic != "NCA3") throw new InvalidDataException("Not an NCA3 file");
head.Distribution = reader.ReadByte(); head.Distribution = reader.ReadByte();
head.ContentType = (ContentType)reader.ReadByte(); head.ContentType = (ContentType)reader.ReadByte();
head.CryptoType = reader.ReadByte(); head.CryptoType = reader.ReadByte();

View file

@ -32,7 +32,7 @@ namespace libhac
try try
{ {
var sdPath = "/" + Util.GetRelativePath(file, ContentsDir).Replace('\\', '/'); 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 = new Nca(Keyset, nax0.Stream);
nca.Name = Path.GetFileName(file); nca.Name = Path.GetFileName(file);
} }