mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Merge pull request #10 from jonnysp/master
added XCI File and Partition HashValidity
This commit is contained in:
commit
54319145c1
5 changed files with 117 additions and 82 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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) { }
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue