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 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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) { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue