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,
|
||||
Romfs,
|
||||
Nax0,
|
||||
Xci,
|
||||
SwitchFs,
|
||||
Save
|
||||
}
|
||||
|
|
|
@ -45,6 +45,9 @@ namespace hactoolnet
|
|||
case FileType.Save:
|
||||
ProcessSave(ctx);
|
||||
break;
|
||||
case FileType.Xci:
|
||||
ProcessXci(ctx);
|
||||
break;
|
||||
default:
|
||||
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)
|
||||
{
|
||||
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
|
|
|
@ -8,6 +8,9 @@ namespace libhac
|
|||
{
|
||||
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)
|
||||
{
|
||||
using (var aes = Aes.Create())
|
||||
|
@ -29,16 +32,38 @@ namespace libhac
|
|||
public static void DecryptEcb(byte[] key, byte[] src, byte[] dest, int 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)
|
||||
{
|
||||
var kek = new byte[0x10];
|
||||
var srcKek = new byte[0x10];
|
||||
DecryptEcb(masterKey, kekSeed, kek, 0x10);
|
||||
DecryptEcb(kek, src, srcKek, 0x10);
|
||||
var kek = new byte[Aes128Size];
|
||||
var srcKek = new byte[Aes128Size];
|
||||
DecryptEcb(masterKey, kekSeed, kek, Aes128Size);
|
||||
DecryptEcb(kek, src, srcKek, Aes128Size);
|
||||
|
||||
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_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[] 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[] xci_header_key { get; set; } = new byte[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[][] 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("titlekek_source", 0x10, set => set.titlekek_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("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("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]),
|
||||
|
|
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