mirror of
https://github.com/SabreTools/NDecrypt.git
synced 2026-09-22 06:45:07 +00:00
Fill in more CIA-specific objects
This commit is contained in:
@@ -66,6 +66,24 @@ namespace NDecrypt.N3DS
|
||||
NewKeyYGenerator = 0x20,
|
||||
}
|
||||
|
||||
internal enum ContentIndex : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Main Content (.CXI for 3DS executable content/.CFA for 3DS Data Archives/.SRL for TWL content)
|
||||
/// </summary>
|
||||
MainContent = 0x0000,
|
||||
|
||||
/// <summary>
|
||||
/// Home Menu Manual (.CFA)
|
||||
/// </summary>
|
||||
HomeMenuManual = 0x0001,
|
||||
|
||||
/// <summary>
|
||||
/// DLP Child Container (.CFA)
|
||||
/// </summary>
|
||||
DLPChildContainer = 0x0002,
|
||||
}
|
||||
|
||||
internal enum ContentPlatform : byte
|
||||
{
|
||||
CTR = 0x01,
|
||||
@@ -165,6 +183,13 @@ namespace NDecrypt.N3DS
|
||||
MediaCardDevice2X = 0x07,
|
||||
}
|
||||
|
||||
internal enum PublicKeyType : uint
|
||||
{
|
||||
RSA_4096 = 0x00000000,
|
||||
RSA_2048 = 0x00000001,
|
||||
ECDSA = 0x00000002,
|
||||
}
|
||||
|
||||
internal enum ResourceLimitCategory
|
||||
{
|
||||
APPLICATION = 0,
|
||||
@@ -173,10 +198,30 @@ namespace NDecrypt.N3DS
|
||||
OTHER = 3,
|
||||
}
|
||||
|
||||
internal enum SignatureType : uint
|
||||
{
|
||||
RSA_4096_SHA1 = 0x010000,
|
||||
RSA_2048_SHA1 = 0x010001,
|
||||
ECDSA_SHA1 = 0x010002,
|
||||
RSA_4096_SHA256 = 0x010003,
|
||||
RSA_2048_SHA256 = 0x010004,
|
||||
ECDSA_SHA256 = 0x010005,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum StorageInfoOtherAttributes : byte
|
||||
{
|
||||
NotUseROMFS = 0x01,
|
||||
UseExtendedSavedataAccess = 0x02,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum TMDContentType : ushort
|
||||
{
|
||||
Encrypted = 0x0001,
|
||||
Disc = 0x0002,
|
||||
CFM = 0x0004,
|
||||
Optional = 0x4000,
|
||||
Shared = 0x8000,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
// https://www.3dbrew.org/wiki/CIA
|
||||
internal class CIAHeader
|
||||
{
|
||||
/// <summary>
|
||||
@@ -44,35 +45,25 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
public long ContentSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Index
|
||||
/// </summary>
|
||||
public byte[] ContentIndex { get; private set; }
|
||||
#region Content Index
|
||||
|
||||
/// <summary>
|
||||
/// Certificate chain
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://www.3dbrew.org/wiki/Certificates
|
||||
/// https://www.3dbrew.org/wiki/Ticket#Certificate_Chain
|
||||
/// https://www.3dbrew.org/wiki/CIA#Certificate_Chain
|
||||
/// </remarks>
|
||||
public byte[] CertificateChain { get; set; }
|
||||
public Certificate[] CertificateChain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ticket
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://www.3dbrew.org/wiki/Ticket#Structure
|
||||
/// </remarks>
|
||||
public byte[] Ticket { get; set; }
|
||||
public Ticket Ticket { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// TMD file data
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://www.3dbrew.org/wiki/Title_metadata
|
||||
/// </remarks>
|
||||
public byte[] TMDFileData { get; set; }
|
||||
public TitleMetadata TMDFileData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content file data
|
||||
@@ -87,6 +78,8 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
public MetaFile MetaFileData { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get a CIA header, if possible
|
||||
/// </summary>
|
||||
@@ -106,11 +99,15 @@ namespace NDecrypt.N3DS.Headers
|
||||
header.TMDFileSize = reader.ReadInt32();
|
||||
header.MetaSize = reader.ReadInt32();
|
||||
header.ContentSize = reader.ReadInt64();
|
||||
header.ContentIndex = reader.ReadBytes(0x2000);
|
||||
|
||||
header.CertificateChain = reader.ReadBytes(header.CertificateChainSize);
|
||||
header.Ticket = reader.ReadBytes(header.TicketSize);
|
||||
header.TMDFileData = reader.ReadBytes(header.TMDFileSize);
|
||||
header.CertificateChain = new Certificate[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
header.CertificateChain[i] = Certificate.Read(reader);
|
||||
}
|
||||
|
||||
header.Ticket = Ticket.Read(reader, header.TicketSize);
|
||||
header.TMDFileData = TitleMetadata.Read(reader, header.TMDFileSize);
|
||||
header.ContentFileData = reader.ReadBytes((int)header.ContentSize);
|
||||
|
||||
if (header.MetaSize > 0)
|
||||
|
||||
137
NDecrypt/N3DS/Headers/Certificate.cs
Normal file
137
NDecrypt/N3DS/Headers/Certificate.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
// https://www.3dbrew.org/wiki/Certificates
|
||||
internal class Certificate
|
||||
{
|
||||
/// <summary>
|
||||
/// Signature Type
|
||||
/// </summary>
|
||||
public SignatureType SignatureType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature size
|
||||
/// </summary>
|
||||
public ushort SignatureSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Padding size
|
||||
/// </summary>
|
||||
public byte PaddingSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature
|
||||
/// </summary>
|
||||
public byte[] Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer
|
||||
/// </summary>
|
||||
public byte[] Issuer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Key Type
|
||||
/// </summary>
|
||||
public PublicKeyType KeyType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name
|
||||
/// </summary>
|
||||
public byte[] Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Expiration time as UNIX Timestamp, used at least for CTCert
|
||||
/// </summary>
|
||||
public uint ExpirationTime { get; private set; }
|
||||
|
||||
// This contains the Public Key(i.e. Modulus & Public Exponent)
|
||||
#region RSA
|
||||
|
||||
/// <summary>
|
||||
/// Modulus
|
||||
/// </summary>
|
||||
public byte[] Modulus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Public Exponent
|
||||
/// </summary>
|
||||
public uint PublicExponent { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
// This contains the ECC public key, and is as follows:
|
||||
#region ECC
|
||||
|
||||
/// <summary>
|
||||
/// Public Key
|
||||
/// </summary>
|
||||
public byte[] PublicKey { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get certificate, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Certificate object, null on error</returns>
|
||||
public static Certificate Read(BinaryReader reader)
|
||||
{
|
||||
Certificate ct = new Certificate();
|
||||
|
||||
try
|
||||
{
|
||||
ct.SignatureType = (SignatureType)reader.ReadUInt32();
|
||||
switch (ct.SignatureType)
|
||||
{
|
||||
case SignatureType.RSA_4096_SHA1:
|
||||
case SignatureType.RSA_4096_SHA256:
|
||||
ct.SignatureSize = 0x200;
|
||||
ct.PaddingSize = 0x3C;
|
||||
break;
|
||||
case SignatureType.RSA_2048_SHA1:
|
||||
case SignatureType.RSA_2048_SHA256:
|
||||
ct.SignatureSize = 0x100;
|
||||
ct.PaddingSize = 0x3C;
|
||||
break;
|
||||
case SignatureType.ECDSA_SHA1:
|
||||
case SignatureType.ECDSA_SHA256:
|
||||
ct.SignatureSize = 0x03C;
|
||||
ct.PaddingSize = 0x40;
|
||||
break;
|
||||
}
|
||||
|
||||
ct.Signature = reader.ReadBytes(ct.SignatureSize);
|
||||
reader.ReadBytes(ct.PaddingSize); // Padding
|
||||
ct.Issuer = reader.ReadBytes(0x40);
|
||||
ct.KeyType = (PublicKeyType)reader.ReadUInt32();
|
||||
ct.Name = reader.ReadBytes(0x40);
|
||||
ct.ExpirationTime = reader.ReadUInt32();
|
||||
|
||||
switch (ct.KeyType)
|
||||
{
|
||||
case PublicKeyType.RSA_4096:
|
||||
ct.Modulus = reader.ReadBytes(0x200);
|
||||
ct.PublicExponent = reader.ReadUInt32();
|
||||
reader.ReadBytes(0x34); // Padding
|
||||
break;
|
||||
case PublicKeyType.RSA_2048:
|
||||
ct.Modulus = reader.ReadBytes(0x100);
|
||||
ct.PublicExponent = reader.ReadUInt32();
|
||||
reader.ReadBytes(0x34); // Padding
|
||||
break;
|
||||
case PublicKeyType.ECDSA:
|
||||
ct.PublicKey = reader.ReadBytes(0x3C);
|
||||
reader.ReadBytes(0x3C); // Padding
|
||||
break;
|
||||
}
|
||||
|
||||
return ct;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
NDecrypt/N3DS/Headers/ContentChunkRecord.cs
Normal file
59
NDecrypt/N3DS/Headers/ContentChunkRecord.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
internal class ContentChunkRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Content id
|
||||
/// </summary>
|
||||
public uint ContentId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content index
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This does not apply to DLC.
|
||||
/// </remarks>
|
||||
public ContentIndex ContentIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content type
|
||||
/// </summary>
|
||||
public TMDContentType ContentType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content size
|
||||
/// </summary>
|
||||
public ulong ContentSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SHA-256 hash
|
||||
/// </summary>
|
||||
public byte[] SHA256Hash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get content chunk record, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Content chunk record object, null on error</returns>
|
||||
public static ContentChunkRecord Read(BinaryReader reader)
|
||||
{
|
||||
ContentChunkRecord ccr = new ContentChunkRecord();
|
||||
|
||||
try
|
||||
{
|
||||
ccr.ContentId = reader.ReadUInt32();
|
||||
ccr.ContentIndex = (ContentIndex)reader.ReadUInt16();
|
||||
ccr.ContentType = (TMDContentType)reader.ReadUInt16();
|
||||
ccr.ContentSize = reader.ReadUInt64();
|
||||
ccr.SHA256Hash = reader.ReadBytes(0x20);
|
||||
return ccr;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
NDecrypt/N3DS/Headers/ContentInfoRecord.cs
Normal file
44
NDecrypt/N3DS/Headers/ContentInfoRecord.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
internal class ContentInfoRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Content index offset
|
||||
/// </summary>
|
||||
public ushort ContentIndexOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content command count [k]
|
||||
/// </summary>
|
||||
public ushort ContentCommandCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SHA-256 hash of the next k content records that have not been hashed yet
|
||||
/// </summary>
|
||||
public byte[] UnhashedContentRecordsSHA256Hash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get content info record, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Content info record object, null on error</returns>
|
||||
public static ContentInfoRecord Read(BinaryReader reader)
|
||||
{
|
||||
ContentInfoRecord cir = new ContentInfoRecord();
|
||||
|
||||
try
|
||||
{
|
||||
cir.ContentIndexOffset = reader.ReadUInt16();
|
||||
cir.ContentCommandCount = reader.ReadUInt16();
|
||||
cir.UnhashedContentRecordsSHA256Hash = reader.ReadBytes(0x20);
|
||||
return cir;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
229
NDecrypt/N3DS/Headers/Ticket.cs
Normal file
229
NDecrypt/N3DS/Headers/Ticket.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
// https://www.3dbrew.org/wiki/Ticket
|
||||
internal class Ticket
|
||||
{
|
||||
/// <summary>
|
||||
/// Signature Type
|
||||
/// </summary>
|
||||
public SignatureType SignatureType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature size
|
||||
/// </summary>
|
||||
public ushort SignatureSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Padding size
|
||||
/// </summary>
|
||||
public byte PaddingSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature
|
||||
/// </summary>
|
||||
public byte[] Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer
|
||||
/// </summary>
|
||||
public byte[] Issuer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ECC PublicKey
|
||||
/// </summary>
|
||||
public byte[] ECCPublicKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version (For 3DS this is always 1)
|
||||
/// </summary>
|
||||
public byte Version { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// CaCrlVersion
|
||||
/// </summary>
|
||||
public byte CaCrlVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SignerCrlVersion
|
||||
/// </summary>
|
||||
public byte SignerCrlVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// TitleKey (normal-key encrypted using one of the common keyYs; see below)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The titlekey is decrypted by using the AES engine with the ticket common-key keyslot.
|
||||
/// The keyY is selected through an index (ticket offset 0xB1) into a plaintext array
|
||||
/// of 6 keys ("common keyYs") stored in the data section of Process9. AES-CBC mode is used
|
||||
/// where the IV is the big-endian titleID. Note that on a retail unit index0 is a retail keyY,
|
||||
/// while on a dev-unit index0 is the dev common-key which is a normal-key.
|
||||
/// (On retail for these keyYs, the hardware key-scrambler is used)
|
||||
///
|
||||
/// The titlekey is used to decrypt content downloaded from the CDN using 128-bit AES-CBC with
|
||||
/// the content index (as big endian u16, padded with trailing zeroes) as the IV.
|
||||
/// </remarks>
|
||||
public byte[] TitleKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// TicketID
|
||||
/// </summary>
|
||||
public ulong TicketID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ConsoleID
|
||||
/// </summary>
|
||||
public uint ConsoleID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// TitleID
|
||||
/// </summary>
|
||||
public ulong TitleID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public ushort Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ticket title version
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Ticket Title Version is generally the same as the title version stored in the
|
||||
/// Title Metadata. Although it doesn't have to match the TMD version to be valid.
|
||||
/// </remarks>
|
||||
public ushort TicketTitleVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public ulong Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// License Type
|
||||
/// </summary>
|
||||
public byte LicenseType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Index to the common keyY used for this ticket, usually 0x1 for retail system titles;
|
||||
/// see below.
|
||||
/// </summary>
|
||||
public byte CommonKeyYIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// eShop Account ID?
|
||||
/// </summary>
|
||||
public uint eShopAccountID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte Reserved5 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Audit
|
||||
/// </summary>
|
||||
public byte Audit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved6 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limits
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In demos, the first u32 in the "Limits" section is 0x4, then the second u32 is the max-playcount.
|
||||
/// </remarks>
|
||||
public byte[] Limits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Index
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Content Index of a ticket has its own size defined within itself,
|
||||
/// with seemingly a minimal of 20 bytes, the second u32 in big endian defines
|
||||
/// the full value of X.
|
||||
/// </remarks>
|
||||
public byte[] ContentIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get ticket, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="ticketSize">Ticket size from the header</param>
|
||||
/// <returns>Ticket object, null on error</returns>
|
||||
public static Ticket Read(BinaryReader reader, int ticketSize)
|
||||
{
|
||||
Ticket tk = new Ticket();
|
||||
|
||||
try
|
||||
{
|
||||
tk.SignatureType = (SignatureType)reader.ReadUInt32();
|
||||
switch (tk.SignatureType)
|
||||
{
|
||||
case SignatureType.RSA_4096_SHA1:
|
||||
case SignatureType.RSA_4096_SHA256:
|
||||
tk.SignatureSize = 0x200;
|
||||
tk.PaddingSize = 0x3C;
|
||||
break;
|
||||
case SignatureType.RSA_2048_SHA1:
|
||||
case SignatureType.RSA_2048_SHA256:
|
||||
tk.SignatureSize = 0x100;
|
||||
tk.PaddingSize = 0x3C;
|
||||
break;
|
||||
case SignatureType.ECDSA_SHA1:
|
||||
case SignatureType.ECDSA_SHA256:
|
||||
tk.SignatureSize = 0x03C;
|
||||
tk.PaddingSize = 0x40;
|
||||
break;
|
||||
}
|
||||
|
||||
tk.Signature = reader.ReadBytes(tk.SignatureSize);
|
||||
reader.ReadBytes(tk.PaddingSize); // Padding
|
||||
tk.Issuer = reader.ReadBytes(0x40);
|
||||
tk.ECCPublicKey = reader.ReadBytes(0x3C);
|
||||
tk.Version = reader.ReadByte();
|
||||
tk.CaCrlVersion = reader.ReadByte();
|
||||
tk.SignerCrlVersion = reader.ReadByte();
|
||||
tk.TitleKey = reader.ReadBytes(0x10);
|
||||
tk.Reserved1 = reader.ReadByte();
|
||||
tk.TicketID = reader.ReadUInt64();
|
||||
tk.ConsoleID = reader.ReadUInt32();
|
||||
tk.TitleID = reader.ReadUInt64();
|
||||
tk.Reserved2 = reader.ReadUInt16();
|
||||
tk.TicketTitleVersion = reader.ReadUInt16();
|
||||
tk.Reserved3 = reader.ReadUInt64();
|
||||
tk.LicenseType = reader.ReadByte();
|
||||
tk.CommonKeyYIndex = reader.ReadByte();
|
||||
tk.Reserved4 = reader.ReadBytes(0x2A);
|
||||
tk.eShopAccountID = reader.ReadUInt32();
|
||||
tk.Reserved5 = reader.ReadByte();
|
||||
tk.Audit = reader.ReadByte();
|
||||
tk.Reserved6 = reader.ReadBytes(0x42);
|
||||
tk.Limits = reader.ReadBytes(0x40);
|
||||
|
||||
if (ticketSize - 0x164 > 0)
|
||||
tk.ContentIndex = reader.ReadBytes(ticketSize - 0x164);
|
||||
|
||||
return tk;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
214
NDecrypt/N3DS/Headers/TitleMetadata.cs
Normal file
214
NDecrypt/N3DS/Headers/TitleMetadata.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
// https://www.3dbrew.org/wiki/Title_metadata
|
||||
internal class TitleMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Signature Type
|
||||
/// </summary>
|
||||
public SignatureType SignatureType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature size
|
||||
/// </summary>
|
||||
public ushort SignatureSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Padding size
|
||||
/// </summary>
|
||||
public byte PaddingSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature
|
||||
/// </summary>
|
||||
public byte[] Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature Issuer
|
||||
/// </summary>
|
||||
public byte[] SignatureIssuer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version
|
||||
/// </summary>
|
||||
public byte Version { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// CaCrlVersion
|
||||
/// </summary>
|
||||
public byte CaCrlVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SignerCrlVersion
|
||||
/// </summary>
|
||||
public byte SignerCrlVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// System Version
|
||||
/// </summary>
|
||||
public ulong SystemVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// TitleID
|
||||
/// </summary>
|
||||
public ulong TitleID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Title Type
|
||||
/// </summary>
|
||||
public uint TitleType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Group ID
|
||||
/// </summary>
|
||||
public ushort GroupID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save Data Size in Little Endian (Bytes) (Also SRL Public Save Data Size)
|
||||
/// </summary>
|
||||
public uint SaveDataSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SRL Private Save Data Size in Little Endian (Bytes)
|
||||
/// </summary>
|
||||
public uint SRLPrivateSaveDataSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public uint Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SRL Flag
|
||||
/// </summary>
|
||||
public byte SRLFlag { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Access Rights
|
||||
/// </summary>
|
||||
public uint AccessRights { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Title Version
|
||||
/// </summary>
|
||||
public ushort TitleVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Count
|
||||
/// </summary>
|
||||
public ushort ContentCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boot Content
|
||||
/// </summary>
|
||||
public ushort BootContent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Padding
|
||||
/// </summary>
|
||||
public ushort Padding { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SHA-256 Hash of the Content Info Records
|
||||
/// </summary>
|
||||
public byte[] SHA256HashContentInfoRecords { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// There are 64 of these records, usually only the first is used.
|
||||
/// </summary>
|
||||
public ContentInfoRecord[] ContentInfoRecords { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// There is one of these for each content contained in this title.
|
||||
/// (Determined by "Content Count" in the TMD Header).
|
||||
/// </summary>
|
||||
public ContentChunkRecord[] ContentChunkRecords { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get ticket metadata, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="metadataSize">Metadata size from the header</param>
|
||||
/// <returns>Title metadata object, null on error</returns>
|
||||
public static TitleMetadata Read(BinaryReader reader, int metadataSize)
|
||||
{
|
||||
TitleMetadata tm = new TitleMetadata();
|
||||
|
||||
try
|
||||
{
|
||||
tm.SignatureType = (SignatureType)reader.ReadUInt32();
|
||||
switch (tm.SignatureType)
|
||||
{
|
||||
case SignatureType.RSA_4096_SHA1:
|
||||
case SignatureType.RSA_4096_SHA256:
|
||||
tm.SignatureSize = 0x200;
|
||||
tm.PaddingSize = 0x3C;
|
||||
break;
|
||||
case SignatureType.RSA_2048_SHA1:
|
||||
case SignatureType.RSA_2048_SHA256:
|
||||
tm.SignatureSize = 0x100;
|
||||
tm.PaddingSize = 0x3C;
|
||||
break;
|
||||
case SignatureType.ECDSA_SHA1:
|
||||
case SignatureType.ECDSA_SHA256:
|
||||
tm.SignatureSize = 0x03C;
|
||||
tm.PaddingSize = 0x40;
|
||||
break;
|
||||
}
|
||||
|
||||
tm.Signature = reader.ReadBytes(tm.SignatureSize);
|
||||
reader.ReadBytes(tm.PaddingSize); // Padding
|
||||
tm.SignatureIssuer = reader.ReadBytes(0x40);
|
||||
tm.Version = reader.ReadByte();
|
||||
tm.CaCrlVersion = reader.ReadByte();
|
||||
tm.SignerCrlVersion = reader.ReadByte();
|
||||
tm.Reserved1 = reader.ReadByte();
|
||||
tm.SystemVersion = reader.ReadUInt64();
|
||||
tm.TitleID = reader.ReadUInt64();
|
||||
tm.TitleType = reader.ReadUInt32();
|
||||
tm.GroupID = reader.ReadUInt16();
|
||||
tm.SaveDataSize = reader.ReadUInt32();
|
||||
tm.SRLPrivateSaveDataSize = reader.ReadUInt32();
|
||||
tm.Reserved2 = reader.ReadUInt32();
|
||||
tm.SRLFlag = reader.ReadByte();
|
||||
tm.Reserved3 = reader.ReadBytes(0x31);
|
||||
tm.AccessRights = reader.ReadUInt32();
|
||||
tm.TitleVersion = reader.ReadUInt16();
|
||||
tm.ContentCount = reader.ReadUInt16();
|
||||
tm.BootContent = reader.ReadUInt16();
|
||||
tm.Padding = reader.ReadUInt16();
|
||||
tm.SHA256HashContentInfoRecords = reader.ReadBytes(0x20);
|
||||
|
||||
tm.ContentInfoRecords = new ContentInfoRecord[64];
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
tm.ContentInfoRecords[i] = ContentInfoRecord.Read(reader);
|
||||
}
|
||||
|
||||
tm.ContentChunkRecords = new ContentChunkRecord[tm.ContentCount];
|
||||
for (int i = 0; i < tm.ContentCount; i++)
|
||||
{
|
||||
tm.ContentChunkRecords[i] = ContentChunkRecord.Read(reader);
|
||||
}
|
||||
|
||||
return tm;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user