diff --git a/LibHac/Crypto.cs b/LibHac/Crypto.cs index 0eca4aae..115ae84e 100644 --- a/LibHac/Crypto.cs +++ b/LibHac/Crypto.cs @@ -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()) @@ -19,13 +30,7 @@ namespace LibHac aes.Key = key; aes.Mode = CipherMode.ECB; aes.Padding = PaddingMode.None; - var dec = aes.CreateDecryptor(); - using (var ms = new MemoryStream(dest, destIndex, length)) - using (var cs = new CryptoStream(ms, dec, CryptoStreamMode.Write)) - { - cs.Write(src, srcIndex, length); - cs.FlushFinalBlock(); - } + Array.Copy(aes.CreateDecryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length); } } @@ -41,13 +46,7 @@ namespace LibHac aes.IV = iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.None; - var dec = aes.CreateDecryptor(); - using (var ms = new MemoryStream(dest, destIndex, length)) - using (var cs = new CryptoStream(ms, dec, CryptoStreamMode.Write)) - { - cs.Write(src, srcIndex, length); - cs.FlushFinalBlock(); - } + Array.Copy(aes.CreateDecryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length); } } @@ -63,13 +62,7 @@ namespace LibHac aes.IV = iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.None; - var dec = aes.CreateEncryptor(); - using (var ms = new MemoryStream(dest, destIndex, length)) - using (var cs = new CryptoStream(ms, dec, CryptoStreamMode.Write)) - { - cs.Write(src, srcIndex, length); - cs.FlushFinalBlock(); - } + Array.Copy(aes.CreateEncryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length); } } diff --git a/LibHac/Pfs.cs b/LibHac/Pfs.cs index 5fc7b573..90dfda57 100644 --- a/LibHac/Pfs.cs +++ b/LibHac/Pfs.cs @@ -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) { diff --git a/LibHac/Xci.cs b/LibHac/Xci.cs index 4f7f0f7f..4c859c9a 100644 --- a/LibHac/Xci.cs +++ b/LibHac/Xci.cs @@ -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) { } } diff --git a/LibHac/XciHeader.cs b/LibHac/XciHeader.cs index f79f7682..a6d367e4 100644 --- a/LibHac/XciHeader.cs +++ b/LibHac/XciHeader.cs @@ -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(); } } diff --git a/hactoolnet/ProcessXci.cs b/hactoolnet/ProcessXci.cs index 612910b9..df721908 100644 --- a/hactoolnet/ProcessXci.cs +++ b/hactoolnet/ProcessXci.cs @@ -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);