added XCI File and Partition HashValidity

This commit is contained in:
jonnysp 2018-09-21 14:37:30 +02:00
parent ab565fe83e
commit 235ace8b1b
5 changed files with 114 additions and 61 deletions

View File

@ -11,6 +11,17 @@ namespace LibHac
internal const int Aes128Size = 0x10;
internal const int Sha256DigestSize = 0x20;
public static bool CheckMemoryHashTable(byte[] data, byte[] hash)
{
bool comp = false;
using (var _SHA = SHA256.Create())
{
comp = Util.ArraysEqual(hash, _SHA.ComputeHash(data));
}
return comp;
}
public static void DecryptEcb(byte[] key, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
{
using (var aes = Aes.Create())

View File

@ -135,6 +135,23 @@ namespace LibHac
reader.BaseStream.Position = stringTableOffset + Files[i].StringTableOffset;
Files[i].Name = reader.ReadAsciiZ();
}
if (Type == PfsType.Hfs0) {
for (int i = 0; i < NumFiles; i++)
{
reader.BaseStream.Position = HeaderSize + Files[i].Offset;
if (Crypto.CheckMemoryHashTable(reader.ReadBytes((int)Files[i].HashedRegionSize), Files[i].Hash))
{
Files[i].HashValidity = Validity.Valid;
}
else
{
Files[i].HashValidity = Validity.Invalid;
}
}
}
}
private static int GetFileEntrySize(PfsType type)
@ -161,6 +178,7 @@ namespace LibHac
public int HashedRegionSize;
public byte[] Hash;
public string Name;
public Validity HashValidity = Validity.Unchecked;
public PfsFileEntry(BinaryReader reader, PfsType type)
{

View File

@ -31,7 +31,8 @@ namespace LibHac
RootPartition = new XciPartition(hfs0Stream)
{
Name = RootPartitionName,
Offset = Header.PartitionFsHeaderAddress
Offset = Header.PartitionFsHeaderAddress,
HashValidity = Header.PartitionFsHeaderValidity
};
Partitions.Add(RootPartition);
@ -43,7 +44,8 @@ namespace LibHac
var partition = new XciPartition(partitionStream)
{
Name = file.Name,
Offset = Header.PartitionFsHeaderAddress + RootPartition.HeaderSize + file.Offset
Offset = Header.PartitionFsHeaderAddress + RootPartition.HeaderSize + file.Offset,
HashValidity = file.HashValidity
};
Partitions.Add(partition);
@ -60,6 +62,7 @@ namespace LibHac
{
public string Name { get; internal set; }
public long Offset { get; internal set; }
public Validity HashValidity { get; set; } = Validity.Unchecked;
public XciPartition(Stream stream) : base(stream) { }
}

View File

@ -64,68 +64,88 @@ namespace LibHac
public Validity SignatureValidity { get; set; }
public Validity PartitionFsHeaderValidity { get; set; } = Validity.Unchecked;
public XciHeader(Keyset keyset, Stream stream)
{
var reader = new BinaryReader(stream, Encoding.Default, true);
Signature = reader.ReadBytes(SignatureSize);
Magic = reader.ReadAscii(4);
if (Magic != HeaderMagic)
{
throw new InvalidDataException("Invalid XCI file: Header magic invalid.");
using (var reader = new BinaryReader(stream, Encoding.Default, true)) {
Signature = reader.ReadBytes(SignatureSize);
Magic = reader.ReadAscii(4);
if (Magic != HeaderMagic)
{
throw new InvalidDataException("Invalid XCI file: Header magic invalid.");
}
reader.BaseStream.Position = SignatureSize;
byte[] sigData = reader.ReadBytes(SignatureSize);
reader.BaseStream.Position = SignatureSize + 4;
if (Crypto.Rsa2048Pkcs1Verify(sigData, Signature, _xciHeaderPubk))
{
SignatureValidity = Validity.Valid;
}
else
{
SignatureValidity = Validity.Invalid;
}
RomAreaStartPage = reader.ReadInt32();
BackupAreaStartPage = reader.ReadInt32();
byte keyIndex = reader.ReadByte();
KekIndex = (byte)(keyIndex >> 4);
TitleKeyDecIndex = (byte)(keyIndex & 7);
RomSize = (RomSize)reader.ReadByte();
CardHeaderVersion = reader.ReadByte();
Flags = (XciFlags)reader.ReadByte();
PackageId = reader.ReadUInt64();
ValidDataEndPage = reader.ReadInt64();
AesCbcIv = reader.ReadBytes(Crypto.Aes128Size);
Array.Reverse(AesCbcIv);
PartitionFsHeaderAddress = reader.ReadInt64();
PartitionFsHeaderSize = reader.ReadInt64();
PartitionFsHeaderHash = reader.ReadBytes(Crypto.Sha256DigestSize);
InitialDataHash = reader.ReadBytes(Crypto.Sha256DigestSize);
SelSec = reader.ReadInt32();
SelT1Key = reader.ReadInt32();
SelKey = reader.ReadInt32();
LimAreaPage = reader.ReadInt32();
if (!keyset.XciHeaderKey.IsEmpty()) {
var encHeader = reader.ReadBytes(EncryptedHeaderSize);
var decHeader = new byte[EncryptedHeaderSize];
Crypto.DecryptCbc(keyset.XciHeaderKey, AesCbcIv, encHeader, decHeader, EncryptedHeaderSize);
using (var decreader = new BinaryReader(new MemoryStream(decHeader))) {
FwVersion = decreader.ReadUInt64();
AccCtrl1 = (CardClockRate)decreader.ReadInt32();
Wait1TimeRead = decreader.ReadInt32();
Wait2TimeRead = decreader.ReadInt32();
Wait1TimeWrite = decreader.ReadInt32();
Wait2TimeWrite = decreader.ReadInt32();
FwMode = decreader.ReadInt32();
UppVersion = decreader.ReadInt32();
decreader.BaseStream.Position += 4;
UppHash = decreader.ReadBytes(8);
UppId = decreader.ReadUInt64();
}
}
reader.BaseStream.Position = PartitionFsHeaderAddress;
if (Crypto.CheckMemoryHashTable(reader.ReadBytes((int)PartitionFsHeaderSize), PartitionFsHeaderHash)) {
PartitionFsHeaderValidity = Validity.Valid;
}
else
{
PartitionFsHeaderValidity = Validity.Invalid;
}
}
reader.BaseStream.Position = SignatureSize;
byte[] sigData = reader.ReadBytes(SignatureSize);
reader.BaseStream.Position = SignatureSize + 4;
if (Crypto.Rsa2048Pkcs1Verify(sigData, Signature, _xciHeaderPubk))
{
SignatureValidity = Validity.Valid;
}
else
{
SignatureValidity = Validity.Invalid;
}
RomAreaStartPage = reader.ReadInt32();
BackupAreaStartPage = reader.ReadInt32();
byte keyIndex = reader.ReadByte();
KekIndex = (byte)(keyIndex >> 4);
TitleKeyDecIndex = (byte)(keyIndex & 7);
RomSize = (RomSize)reader.ReadByte();
CardHeaderVersion = reader.ReadByte();
Flags = (XciFlags)reader.ReadByte();
PackageId = reader.ReadUInt64();
ValidDataEndPage = reader.ReadInt64();
AesCbcIv = reader.ReadBytes(Crypto.Aes128Size);
Array.Reverse(AesCbcIv);
PartitionFsHeaderAddress = reader.ReadInt64();
PartitionFsHeaderSize = reader.ReadInt64();
PartitionFsHeaderHash = reader.ReadBytes(Crypto.Sha256DigestSize);
InitialDataHash = reader.ReadBytes(Crypto.Sha256DigestSize);
SelSec = reader.ReadInt32();
SelT1Key = reader.ReadInt32();
SelKey = reader.ReadInt32();
LimAreaPage = reader.ReadInt32();
if (keyset.XciHeaderKey.IsEmpty()) return;
var encHeader = reader.ReadBytes(EncryptedHeaderSize);
var decHeader = new byte[EncryptedHeaderSize];
Crypto.DecryptCbc(keyset.XciHeaderKey, AesCbcIv, encHeader, decHeader, EncryptedHeaderSize);
reader = new BinaryReader(new MemoryStream(decHeader));
FwVersion = reader.ReadUInt64();
AccCtrl1 = (CardClockRate)reader.ReadInt32();
Wait1TimeRead = reader.ReadInt32();
Wait2TimeRead = reader.ReadInt32();
Wait1TimeWrite = reader.ReadInt32();
Wait2TimeWrite = reader.ReadInt32();
FwMode = reader.ReadInt32();
UppVersion = reader.ReadInt32();
reader.BaseStream.Position += 4;
UppHash = reader.ReadBytes(8);
UppId = reader.ReadUInt64();
}
}

View File

@ -155,6 +155,7 @@ namespace hactoolnet
PrintItem(sb, colLen, "Magic:", xci.Header.Magic);
PrintItem(sb, colLen, $"Header Signature:{xci.Header.SignatureValidity.GetValidityString()}", xci.Header.Signature);
PrintItem(sb, colLen, $"Header Hash:{xci.Header.PartitionFsHeaderValidity.GetValidityString()}", xci.Header.PartitionFsHeaderHash);
PrintItem(sb, colLen, "Cartridge Type:", GetCartridgeType(xci.Header.RomSize));
PrintItem(sb, colLen, "Cartridge Size:", $"0x{Util.MediaToReal(xci.Header.ValidDataEndPage + 1):x12}");
PrintItem(sb, colLen, "Header IV:", xci.Header.AesCbcIv);
@ -172,7 +173,7 @@ namespace hactoolnet
{
const int fileNameLen = 57;
sb.AppendLine($"{GetDisplayName(partition.Name)} Partition:");
sb.AppendLine($"{GetDisplayName(partition.Name)} Partition:{partition.HashValidity.GetValidityString()}");
PrintItem(sb, colLen, " Magic:", partition.Header.Magic);
PrintItem(sb, colLen, " Offset:", $"{partition.Offset:x12}");
PrintItem(sb, colLen, " Number of files:", partition.Files.Length);
@ -184,7 +185,7 @@ namespace hactoolnet
PfsFileEntry file = partition.Files[i];
string label = i == 0 ? " Files:" : "";
string offsets = $"{file.Offset:x12}-{file.Offset + file.Size:x12}";
string offsets = $"{file.Offset:x12}-{file.Offset + file.Size:x12}{file.HashValidity.GetValidityString()}";
string data = $"{partition.Name}:/{file.Name}".PadRight(fileNameLen) + offsets;
PrintItem(sb, colLen, label, data);