Avoid false positives in detection of unencrypted NCAs

This commit is contained in:
Alex Barney 2020-08-15 00:31:17 -07:00
parent a07e17c369
commit 30273bf3ef
2 changed files with 46 additions and 20 deletions

View file

@ -14,7 +14,7 @@ namespace LibHac.FsSystem.NcaUtils
public class Nca
{
private Keyset Keyset { get; }
private bool IsEncrypted { get; set; }
private bool IsEncrypted => Header.IsEncrypted;
private byte[] Nca0KeyArea { get; set; }
private IStorage Nca0TransformedBody { get; set; }
@ -25,13 +25,9 @@ namespace LibHac.FsSystem.NcaUtils
public Nca(Keyset keyset, IStorage storage)
{
Span<byte> magic = stackalloc byte[3];
storage.Read(0x200, magic);
IsEncrypted = magic[0] != 'N' && magic[1] != 'C' && magic[2] != 'A';
Keyset = keyset;
BaseStorage = storage;
Header = IsEncrypted ? new NcaHeader(keyset, storage) : new NcaHeader(storage);
Header = new NcaHeader(keyset, storage);
}
public byte[] GetDecryptedKey(int index)

View file

@ -4,6 +4,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Crypto;
using LibHac.Diag;
using LibHac.Fs;
namespace LibHac.FsSystem.NcaUtils
@ -17,21 +18,15 @@ namespace LibHac.FsSystem.NcaUtils
private readonly Memory<byte> _header;
public NcaVersion FormatVersion { get; private set; }
public NcaHeader(IStorage headerStorage)
{
_header = new byte[HeaderSize];
Span<byte> headerSpan = _header.Span;
headerStorage.Read(0, headerSpan).ThrowIfFailure();
FormatVersion = DetectNcaVersion(headerSpan);
}
public NcaVersion FormatVersion { get; }
public bool IsEncrypted { get; }
public NcaHeader(Keyset keyset, IStorage headerStorage)
{
_header = DecryptHeader(keyset, headerStorage);
(byte[] header, bool isEncrypted) = DecryptHeader(keyset, headerStorage);
_header = header;
IsEncrypted = isEncrypted;
FormatVersion = DetectNcaVersion(_header.Span);
}
@ -200,10 +195,22 @@ namespace LibHac.FsSystem.NcaUtils
return (long)blockIndex * BlockSize;
}
public static byte[] DecryptHeader(Keyset keyset, IStorage storage)
private static (byte[] header, bool isEncrypted) DecryptHeader(Keyset keyset, IStorage storage)
{
var buf = new byte[HeaderSize];
storage.Read(0, buf);
storage.Read(0, buf).ThrowIfFailure();
if (CheckIfDecrypted(buf))
{
int decVersion = buf[0x203] - '0';
if (decVersion != 0 && decVersion != 2 && decVersion != 3)
{
throw new NotSupportedException($"NCA version {decVersion} is not supported.");
}
return (buf, false);
}
byte[] key1 = keyset.HeaderKey.AsSpan(0, 0x10).ToArray();
byte[] key2 = keyset.HeaderKey.AsSpan(0x10, 0x10).ToArray();
@ -240,7 +247,30 @@ namespace LibHac.FsSystem.NcaUtils
throw new NotSupportedException($"NCA version {version} is not supported.");
}
return buf;
return (buf, true);
}
private static bool CheckIfDecrypted(ReadOnlySpan<byte> header)
{
Assert.AssertTrue(header.Length >= 0x400);
// Check the magic value
if (header[0x200] != 'N' || header[0x201] != 'C' || header[0x202] != 'A')
return false;
// Check the version in the magic value
if (!StringUtils.IsDigit(header[0x203]))
return false;
// Is the distribution type valid?
if (header[0x204] > (int)DistributionType.GameCard)
return false;
// Is the content type valid?
if (header[0x205] > (int)NcaContentType.PublicData)
return false;
return true;
}
private static NcaVersion DetectNcaVersion(ReadOnlySpan<byte> header)