Add NCA2 support

This commit is contained in:
Alex Barney 2019-03-15 20:42:58 -05:00
parent 56c4554d81
commit c267826dd1
5 changed files with 63 additions and 5 deletions

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LibHac.IO.RomFs;
@ -7,6 +8,9 @@ namespace LibHac.IO.NcaUtils
{
public class Nca : IDisposable
{
private const int HeaderSize = 0xc00;
private const int HeaderSectorSize = 0x200;
public NcaHeader Header { get; }
public string NcaId { get; set; }
public string Filename { get; set; }
@ -334,9 +338,9 @@ namespace LibHac.IO.NcaUtils
return new NcaHeader(new BinaryReader(OpenHeaderStorage().AsStream()), Keyset);
}
private CachedStorage OpenHeaderStorage()
public IStorage OpenHeaderStorage()
{
long size = 0xc00;
long size = HeaderSize;
// Encrypted portion continues until the first section
if (Sections.Any(x => x != null))
@ -344,7 +348,42 @@ namespace LibHac.IO.NcaUtils
size = Sections.Where(x => x != null).Min(x => x.Offset);
}
return new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, size), Keyset.HeaderKey, 0x200, true), 1, true);
IStorage header = new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, size), Keyset.HeaderKey, HeaderSectorSize, true), 1, true);
int version = ReadHeaderVersion(header);
if (version == 2)
{
header = OpenNca2Header(size);
}
return header;
}
private int ReadHeaderVersion(IStorage header)
{
if (Header != null)
{
return Header.Version;
}
else
{
Span<byte> buf = stackalloc byte[1];
header.Read(buf, 0x203);
return buf[0] - '0';
}
}
private IStorage OpenNca2Header(long size)
{
var sources = new List<IStorage>();
sources.Add(new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, 0x400), Keyset.HeaderKey, HeaderSectorSize, true), 1, true));
for (int i = 0x400; i < size; i += HeaderSectorSize)
{
sources.Add(new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(i, HeaderSectorSize), Keyset.HeaderKey, HeaderSectorSize, true), 1, true));
}
return new ConcatenationStorage(sources, true);
}
private void DecryptKeyArea(Keyset keyset)

View file

@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
namespace LibHac.IO.NcaUtils
{
@ -7,6 +8,7 @@ namespace LibHac.IO.NcaUtils
public byte[] Signature1; // RSA-PSS signature over header with fixed key.
public byte[] Signature2; // RSA-PSS signature over header with key in NPDM.
public string Magic;
public int Version;
public DistributionType Distribution; // System vs gamecard.
public ContentType ContentType;
public byte CryptoType; // Which keyblob (field 1)
@ -33,7 +35,12 @@ namespace LibHac.IO.NcaUtils
Signature1 = reader.ReadBytes(0x100);
Signature2 = reader.ReadBytes(0x100);
Magic = reader.ReadAscii(4);
if (Magic != "NCA3") throw new InvalidDataException("Not an NCA3 file");
if (!Magic.StartsWith("NCA") || Magic[3] < '0' || Magic[3] > '9')
throw new InvalidDataException("Unable to decrypt NCA header, or the file is not an NCA file.");
Version = Magic[3] - '0';
if (Version != 2 && Version != 3) throw new NotSupportedException($"NCA version {Version} is not supported.");
reader.BaseStream.Position -= 4;
SignatureData = reader.ReadBytes(0x200);

View file

@ -26,6 +26,7 @@ namespace hactoolnet
new CliOption("section1dir", 1, (o, a) => o.SectionOutDir[1] = a[0]),
new CliOption("section2dir", 1, (o, a) => o.SectionOutDir[2] = a[0]),
new CliOption("section3dir", 1, (o, a) => o.SectionOutDir[3] = a[0]),
new CliOption("header", 1, (o, a) => o.HeaderOut = a[0]),
new CliOption("exefs", 1, (o, a) => o.ExefsOut = a[0]),
new CliOption("exefsdir", 1, (o, a) => o.ExefsOutDir = a[0]),
new CliOption("romfs", 1, (o, a) => o.RomfsOut = a[0]),
@ -169,6 +170,7 @@ namespace hactoolnet
sb.AppendLine(" --titlekeys <file> Load title keys from an external file.");
sb.AppendLine("NCA options:");
sb.AppendLine(" --plaintext <file> Specify file path for saving a decrypted copy of the NCA.");
sb.AppendLine(" --header <file> Specify Header file path.");
sb.AppendLine(" --section0 <file> Specify Section 0 file path.");
sb.AppendLine(" --section1 <file> Specify Section 1 file path.");
sb.AppendLine(" --section2 <file> Specify Section 2 file path.");

View file

@ -17,6 +17,7 @@ namespace hactoolnet
public string ConsoleKeyFile;
public string[] SectionOut = new string[4];
public string[] SectionOutDir = new string[4];
public string HeaderOut;
public string ExefsOut;
public string ExefsOutDir;
public string RomfsOut;

View file

@ -15,6 +15,15 @@ namespace hactoolnet
using (IStorage file = new LocalStorage(ctx.Options.InFile, FileAccess.Read))
{
var nca = new Nca(ctx.Keyset, file, false);
if (ctx.Options.HeaderOut != null)
{
using (var outHeader = new FileStream(ctx.Options.HeaderOut, FileMode.Create, FileAccess.ReadWrite))
{
nca.OpenHeaderStorage().Slice(0, 0xc00).CopyToStream(outHeader);
}
}
nca.ValidateMasterHashes();
nca.ParseNpdm();