mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Read XCI header
This commit is contained in:
parent
55031755a8
commit
60a8a7b2d3
6 changed files with 180 additions and 8 deletions
|
@ -36,6 +36,7 @@ namespace hactoolnet
|
||||||
Pfs0,
|
Pfs0,
|
||||||
Romfs,
|
Romfs,
|
||||||
Nax0,
|
Nax0,
|
||||||
|
Xci,
|
||||||
SwitchFs,
|
SwitchFs,
|
||||||
Save
|
Save
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,9 @@ namespace hactoolnet
|
||||||
case FileType.Save:
|
case FileType.Save:
|
||||||
ProcessSave(ctx);
|
ProcessSave(ctx);
|
||||||
break;
|
break;
|
||||||
|
case FileType.Xci:
|
||||||
|
ProcessXci(ctx);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
|
@ -184,6 +187,14 @@ namespace hactoolnet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ProcessXci(Context ctx)
|
||||||
|
{
|
||||||
|
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
var xci = new Xci(ctx.Keyset, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void OpenKeyset(Context ctx)
|
private static void OpenKeyset(Context ctx)
|
||||||
{
|
{
|
||||||
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||||
|
|
|
@ -8,6 +8,9 @@ namespace libhac
|
||||||
{
|
{
|
||||||
public class Crypto
|
public class Crypto
|
||||||
{
|
{
|
||||||
|
internal const int Aes128Size = 0x10;
|
||||||
|
internal const int Sha256DigestSize = 0x20;
|
||||||
|
|
||||||
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())
|
||||||
|
@ -29,16 +32,38 @@ namespace libhac
|
||||||
public static void DecryptEcb(byte[] key, byte[] src, byte[] dest, int length) =>
|
public static void DecryptEcb(byte[] key, byte[] src, byte[] dest, int length) =>
|
||||||
DecryptEcb(key, src, 0, dest, 0, length);
|
DecryptEcb(key, src, 0, dest, 0, length);
|
||||||
|
|
||||||
|
public static void DecryptCbc(byte[] key, byte[] iv, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
|
||||||
|
{
|
||||||
|
using (var aes = Aes.Create())
|
||||||
|
{
|
||||||
|
if (aes == null) throw new CryptographicException("Unable to create AES object");
|
||||||
|
aes.Key = key;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DecryptCbc(byte[] key, byte[] iv, byte[] src, byte[] dest, int length) =>
|
||||||
|
DecryptCbc(key, iv, src, 0, dest, 0, length);
|
||||||
|
|
||||||
public static void GenerateKek(byte[] dst, byte[] src, byte[] masterKey, byte[] kekSeed, byte[] keySeed)
|
public static void GenerateKek(byte[] dst, byte[] src, byte[] masterKey, byte[] kekSeed, byte[] keySeed)
|
||||||
{
|
{
|
||||||
var kek = new byte[0x10];
|
var kek = new byte[Aes128Size];
|
||||||
var srcKek = new byte[0x10];
|
var srcKek = new byte[Aes128Size];
|
||||||
DecryptEcb(masterKey, kekSeed, kek, 0x10);
|
DecryptEcb(masterKey, kekSeed, kek, Aes128Size);
|
||||||
DecryptEcb(kek, src, srcKek, 0x10);
|
DecryptEcb(kek, src, srcKek, Aes128Size);
|
||||||
|
|
||||||
if (keySeed != null)
|
if (keySeed != null)
|
||||||
{
|
{
|
||||||
DecryptEcb(srcKek, keySeed, dst, 0x10);
|
DecryptEcb(srcKek, keySeed, dst, Aes128Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,9 @@ namespace libhac
|
||||||
public byte[] sd_card_kek_source { get; set; } = new byte[0x10];
|
public byte[] sd_card_kek_source { get; set; } = new byte[0x10];
|
||||||
public byte[][] sd_card_key_sources { get; set; } = Util.CreateJaggedArray<byte[][]>(2, 0x20);
|
public byte[][] sd_card_key_sources { get; set; } = Util.CreateJaggedArray<byte[][]>(2, 0x20);
|
||||||
public byte[][] sd_card_key_sources_specific { get; set; } = Util.CreateJaggedArray<byte[][]>(2, 0x20);
|
public byte[][] sd_card_key_sources_specific { get; set; } = Util.CreateJaggedArray<byte[][]>(2, 0x20);
|
||||||
public byte[] encrypted_header_key { get; set; } = new byte[0x20];
|
public byte[] header_key_source { get; set; } = new byte[0x20];
|
||||||
public byte[] header_key { get; set; } = new byte[0x20];
|
public byte[] header_key { get; set; } = new byte[0x20];
|
||||||
|
public byte[] xci_header_key { get; set; } = new byte[0x10];
|
||||||
public byte[][] titlekeks { get; set; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
public byte[][] titlekeks { get; set; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||||
public byte[][][] key_area_keys { get; set; } = Util.CreateJaggedArray<byte[][][]>(0x20, 3, 0x10);
|
public byte[][][] key_area_keys { get; set; } = Util.CreateJaggedArray<byte[][][]>(0x20, 3, 0x10);
|
||||||
public byte[][] sd_card_keys { get; set; } = Util.CreateJaggedArray<byte[][]>(2, 0x20);
|
public byte[][] sd_card_keys { get; set; } = Util.CreateJaggedArray<byte[][]>(2, 0x20);
|
||||||
|
@ -177,9 +178,10 @@ namespace libhac
|
||||||
new KeyValue("key_area_key_system_source", 0x10, set => set.key_area_key_system_source),
|
new KeyValue("key_area_key_system_source", 0x10, set => set.key_area_key_system_source),
|
||||||
new KeyValue("titlekek_source", 0x10, set => set.titlekek_source),
|
new KeyValue("titlekek_source", 0x10, set => set.titlekek_source),
|
||||||
new KeyValue("header_kek_source", 0x10, set => set.header_kek_source),
|
new KeyValue("header_kek_source", 0x10, set => set.header_kek_source),
|
||||||
new KeyValue("header_key_source", 0x20, set => set.encrypted_header_key),
|
new KeyValue("header_key_source", 0x20, set => set.header_key_source),
|
||||||
new KeyValue("header_key", 0x20, set => set.header_key),
|
new KeyValue("header_key", 0x20, set => set.header_key),
|
||||||
new KeyValue("encrypted_header_key", 0x20, set => set.encrypted_header_key),
|
new KeyValue("xci_header_key", 0x10, set => set.xci_header_key),
|
||||||
|
new KeyValue("encrypted_header_key", 0x20, set => set.header_key_source),
|
||||||
new KeyValue("package2_key_source", 0x10, set => set.package2_key_source),
|
new KeyValue("package2_key_source", 0x10, set => set.package2_key_source),
|
||||||
new KeyValue("sd_card_kek_source", 0x10, set => set.sd_card_kek_source),
|
new KeyValue("sd_card_kek_source", 0x10, set => set.sd_card_kek_source),
|
||||||
new KeyValue("sd_card_nca_key_source", 0x20, set => set.sd_card_key_sources[1]),
|
new KeyValue("sd_card_nca_key_source", 0x20, set => set.sd_card_key_sources[1]),
|
||||||
|
|
13
libhac/Xci.cs
Normal file
13
libhac/Xci.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace libhac
|
||||||
|
{
|
||||||
|
public class Xci
|
||||||
|
{
|
||||||
|
public XciHeader Header { get; set; }
|
||||||
|
public Xci(Keyset keyset, Stream stream)
|
||||||
|
{
|
||||||
|
Header = new XciHeader(keyset, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
libhac/XciHeader.cs
Normal file
120
libhac/XciHeader.cs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace libhac
|
||||||
|
{
|
||||||
|
public class XciHeader
|
||||||
|
{
|
||||||
|
private const int SignatureSize = 0x100;
|
||||||
|
private const string HeaderMagic = "HEAD";
|
||||||
|
private const int EncryptedHeaderSize = 0x70;
|
||||||
|
|
||||||
|
public byte[] Signature { get; set; }
|
||||||
|
public string Magic { get; set; }
|
||||||
|
public int RomAreaStartPage { get; set; }
|
||||||
|
public int BackupAreaStartPage { get; set; }
|
||||||
|
public byte KekIndex { get; set; }
|
||||||
|
public byte TitleKeyDecIndex { get; set; }
|
||||||
|
public RomSize RomSize { get; set; }
|
||||||
|
public byte CardHeaderVersion { get; set; }
|
||||||
|
public XciFlags Flags { get; set; }
|
||||||
|
public ulong PackageId { get; set; }
|
||||||
|
public long ValidDataEndPage { get; set; }
|
||||||
|
public byte[] AesCbcIv { get; set; }
|
||||||
|
public long PartitionFsHeaderAddress { get; set; }
|
||||||
|
public long PartitionFsHeaderSize { get; set; }
|
||||||
|
public byte[] PartitionFsHeaderHash { get; set; }
|
||||||
|
public byte[] InitialDataHash { get; set; }
|
||||||
|
public int SelSec { get; set; }
|
||||||
|
public int SelT1Key { get; set; }
|
||||||
|
public int SelKey { get; set; }
|
||||||
|
public int LimAreaPage { get; set; }
|
||||||
|
|
||||||
|
public ulong FwVersion { get; set; }
|
||||||
|
public CardClockRate AccCtrl1 { get; set; }
|
||||||
|
public int Wait1TimeRead { get; set; }
|
||||||
|
public int Wait2TimeRead { get; set; }
|
||||||
|
public int Wait1TimeWrite { get; set; }
|
||||||
|
public int Wait2TimeWrite { get; set; }
|
||||||
|
public int FwMode { get; set; }
|
||||||
|
public int UppVersion { get; set; }
|
||||||
|
public byte[] UppHash { get; set; }
|
||||||
|
public ulong UppId { get; set; }
|
||||||
|
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
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.xci_header_key.IsEmpty()) return;
|
||||||
|
|
||||||
|
var encHeader = reader.ReadBytes(EncryptedHeaderSize);
|
||||||
|
var decHeader = new byte[EncryptedHeaderSize];
|
||||||
|
Crypto.DecryptCbc(keyset.xci_header_key, 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RomSize
|
||||||
|
{
|
||||||
|
Size1Gb = 0xFA,
|
||||||
|
Size2Gb = 0xF8,
|
||||||
|
Size4Gb = 0xF0,
|
||||||
|
Size8Gb = 0xE0,
|
||||||
|
Size16Gb = 0xE1,
|
||||||
|
Size32Gb = 0xE2
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum XciFlags
|
||||||
|
{
|
||||||
|
AutoBoot = 1 << 0,
|
||||||
|
HistoryErase = 1 << 1,
|
||||||
|
RepairTool = 1 << 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CardClockRate
|
||||||
|
{
|
||||||
|
ClockRate25 = 10551312,
|
||||||
|
ClockRate50
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue