mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Have Nax0 dispose stream
This commit is contained in:
parent
88f72a3527
commit
32996aed3c
5 changed files with 103 additions and 64 deletions
|
@ -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("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,8 +74,8 @@ namespace libhac
|
||||||
SystemUpdate,
|
SystemUpdate,
|
||||||
FirmwarePackageA,
|
FirmwarePackageA,
|
||||||
FirmwarePackageB,
|
FirmwarePackageB,
|
||||||
RegularApplication = 0x80,
|
Application = 0x80,
|
||||||
UpdateTitle,
|
Patch,
|
||||||
AddOnContent,
|
AddOnContent,
|
||||||
DeltaTitle
|
DeltaTitle
|
||||||
}
|
}
|
||||||
|
|
125
libhac/Nax0.cs
125
libhac/Nax0.cs
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue