Merge pull request #10 from jonnysp/master

added XCI File and Partition HashValidity
This commit is contained in:
Alex Barney 2018-09-24 13:40:03 -06:00 committed by GitHub
commit 54319145c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 82 deletions

View file

@ -11,6 +11,17 @@ namespace LibHac
internal const int Aes128Size = 0x10; internal const int Aes128Size = 0x10;
internal const int Sha256DigestSize = 0x20; 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) public static void DecryptEcb(byte[] key, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
{ {
using (var aes = Aes.Create()) using (var aes = Aes.Create())
@ -19,13 +30,7 @@ namespace LibHac
aes.Key = key; aes.Key = key;
aes.Mode = CipherMode.ECB; aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None; aes.Padding = PaddingMode.None;
var dec = aes.CreateDecryptor(); Array.Copy(aes.CreateDecryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length);
using (var ms = new MemoryStream(dest, destIndex, length))
using (var cs = new CryptoStream(ms, dec, CryptoStreamMode.Write))
{
cs.Write(src, srcIndex, length);
cs.FlushFinalBlock();
}
} }
} }
@ -41,13 +46,7 @@ namespace LibHac
aes.IV = iv; aes.IV = iv;
aes.Mode = CipherMode.CBC; aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None; aes.Padding = PaddingMode.None;
var dec = aes.CreateDecryptor(); Array.Copy(aes.CreateDecryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length);
using (var ms = new MemoryStream(dest, destIndex, length))
using (var cs = new CryptoStream(ms, dec, CryptoStreamMode.Write))
{
cs.Write(src, srcIndex, length);
cs.FlushFinalBlock();
}
} }
} }
@ -63,13 +62,7 @@ namespace LibHac
aes.IV = iv; aes.IV = iv;
aes.Mode = CipherMode.CBC; aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None; aes.Padding = PaddingMode.None;
var dec = aes.CreateEncryptor(); Array.Copy(aes.CreateEncryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length);
using (var ms = new MemoryStream(dest, destIndex, length))
using (var cs = new CryptoStream(ms, dec, CryptoStreamMode.Write))
{
cs.Write(src, srcIndex, length);
cs.FlushFinalBlock();
}
} }
} }

View file

@ -135,6 +135,23 @@ namespace LibHac
reader.BaseStream.Position = stringTableOffset + Files[i].StringTableOffset; reader.BaseStream.Position = stringTableOffset + Files[i].StringTableOffset;
Files[i].Name = reader.ReadAsciiZ(); 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) private static int GetFileEntrySize(PfsType type)
@ -161,6 +178,7 @@ namespace LibHac
public int HashedRegionSize; public int HashedRegionSize;
public byte[] Hash; public byte[] Hash;
public string Name; public string Name;
public Validity HashValidity = Validity.Unchecked;
public PfsFileEntry(BinaryReader reader, PfsType type) public PfsFileEntry(BinaryReader reader, PfsType type)
{ {

View file

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

View file

@ -64,68 +64,88 @@ namespace LibHac
public Validity SignatureValidity { get; set; } public Validity SignatureValidity { get; set; }
public Validity PartitionFsHeaderValidity { get; set; } = Validity.Unchecked;
public XciHeader(Keyset keyset, Stream stream) public XciHeader(Keyset keyset, Stream stream)
{ {
var reader = new BinaryReader(stream, Encoding.Default, true);
Signature = reader.ReadBytes(SignatureSize); using (var reader = new BinaryReader(stream, Encoding.Default, true)) {
Magic = reader.ReadAscii(4);
if (Magic != HeaderMagic) Signature = reader.ReadBytes(SignatureSize);
{ Magic = reader.ReadAscii(4);
throw new InvalidDataException("Invalid XCI file: Header magic invalid."); 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, "Magic:", xci.Header.Magic);
PrintItem(sb, colLen, $"Header Signature:{xci.Header.SignatureValidity.GetValidityString()}", xci.Header.Signature); 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 Type:", GetCartridgeType(xci.Header.RomSize));
PrintItem(sb, colLen, "Cartridge Size:", $"0x{Util.MediaToReal(xci.Header.ValidDataEndPage + 1):x12}"); PrintItem(sb, colLen, "Cartridge Size:", $"0x{Util.MediaToReal(xci.Header.ValidDataEndPage + 1):x12}");
PrintItem(sb, colLen, "Header IV:", xci.Header.AesCbcIv); PrintItem(sb, colLen, "Header IV:", xci.Header.AesCbcIv);
@ -172,7 +173,7 @@ namespace hactoolnet
{ {
const int fileNameLen = 57; 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, " Magic:", partition.Header.Magic);
PrintItem(sb, colLen, " Offset:", $"{partition.Offset:x12}"); PrintItem(sb, colLen, " Offset:", $"{partition.Offset:x12}");
PrintItem(sb, colLen, " Number of files:", partition.Files.Length); PrintItem(sb, colLen, " Number of files:", partition.Files.Length);
@ -184,7 +185,7 @@ namespace hactoolnet
PfsFileEntry file = partition.Files[i]; PfsFileEntry file = partition.Files[i];
string label = i == 0 ? " Files:" : ""; 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; string data = $"{partition.Name}:/{file.Name}".PadRight(fileNameLen) + offsets;
PrintItem(sb, colLen, label, data); PrintItem(sb, colLen, label, data);