Skip to content

Instantly share code, notes, and snippets.

@Subv
Created December 3, 2014 03:01
Show Gist options
  • Save Subv/3e0cefdd3e267ce883a5 to your computer and use it in GitHub Desktop.
Save Subv/3e0cefdd3e267ce883a5 to your computer and use it in GitHub Desktop.
010 Editor Nintendo 3DS CIA Template
//--------------------------------------
//--- 010 Editor v5.0 Binary Template
//
// File: 3DSCIATemplate.bt
// Author: Subv
// Revision: 0.1
// Purpose: Parsing .CIA files for the Nintendo 3DS
//--------------------------------------
struct CIAFile
{
struct CIAHeader
{
int ArchiveHeaderSize<format=hex>;
short Type;
short Version;
int CertificateChainSize;
int TicketSize;
int TMDFileSize;
int MetaSize;
int64 ContentSize;
byte ContentIndex[0x2000];
} Header;
byte Padding1[(64 - FTell() % 64) % 64];
enum SignatureTypes
{
RSA_4096_SHA1 = 0x010000,
RSA_2048_SHA1 = 0x010001,
ELLIPTIC_CURVE_SHA1 = 0x010002,
RSA_4096_SHA2 = 0x010003,
RSA_2048_SHA2 = 0x010004,
ECDSA_SHA2 = 0x010005
};
enum KeyTypes
{
RSA_4096 = 0,
RSA_2048 = 1,
ELLIPTIC_CURVE = 2
};
struct SignatureDataStruct
{
SignatureTypes SignatureType;
if (SignatureType == RSA_4096_SHA1 || SignatureType == RSA_4096_SHA2)
byte Signature[0x200 + 0x3C]<format=hex>;
else if (SignatureType == RSA_2048_SHA1 || SignatureType == RSA_2048_SHA2)
byte Signature[0x100 + 0x3C]<format=hex>;
else if (SignatureType == ELLIPTIC_CURVE_SHA1 || SignatureType == ECDSA_SHA2)
byte Signature[0x3C + 0x40]<format=hex>;
};
BigEndian();
struct CertificateStruct
{
SignatureDataStruct SignatureData;
char Issuer[0x40]<format=hex>;
KeyTypes KeyType;
char Name[0x40];
int Unknown;
if (KeyType == RSA_4096)
{
byte Modulus[0x200];
int PublicExponent;
byte Padding[0x34];
}
else if (KeyType == RSA_2048)
{
byte Modulus[0x100];
int PublicExponent;
byte Padding[0x34];
}
else if (KeyType == ELLIPTIC_CURVE)
{
byte PublicKey[0x3C];
byte Padding[0x3C];
}
} Certificates[3] <optimize=false>;
Assert(sizeof(Certificates[0]) + sizeof(Certificates[1]) + sizeof(Certificates[2]) == Header.CertificateChainSize);
byte Padding2[(64 - FTell() % 64) % 64];
struct TicketStructure
{
SignatureDataStruct SignatureData;
char Issuer[0x40];
byte ECCPublicKey[0x3C];
byte Version;
byte CaCrlVersion;
byte SignerCrlVersion;
byte EncryptedTitleKey[0x10];
byte Reserved0;
int64 TicketID;
int ConsoleID;
int64 TitleID;
byte Reserved1[0x2];
short TicketTitleVersion;
byte Reserved2[0x8];
byte LicenseType;
byte TicketCommonKeyYIndex;
byte Reserved3[0x2A];
int EShopAccountID;
byte Reserved4;
byte Audit;
byte Reserved5[0x42];
byte Limits[0x40];
byte ContentIndex[0xAC];
} Ticket;
Assert(sizeof(Ticket) == Header.TicketSize);
byte Padding3[(64 - FTell() % 64) % 64];
struct TitleMetadata
{
SignatureDataStruct SignatureData;
struct TMDHeader
{
char SignatureIssuer[0x40];
byte Version;
byte CACrlVersion;
byte SignerCrlVersion;
byte Reserved;
int64 SystemVersion;
int64 TitleID;
int TitleType;
short GroupID;
int SaveDataSize;
int SRLPrivateSaveDataSize;
int Reserved2;
byte SRLFlag;
byte Reserved3[0x31];
int AccessRights<format=hex>;
short TitleVersion;
short ContentCount;
short BootContent;
short Padding;
byte ContentInfoRecordsHash[0x20];
} Header;
struct ContentInfoRecord
{
short ContentIndexOffset;
short ContentCommandCount;
byte Hash[0x20];
} ContentInfoRecords[64];
enum <short> ContentIndexes
{
MainContent = 0,
HomeMenuManual = 1,
DLChildContainer = 2
};
struct ContentChunkRecord
{
int ContentID;
ContentIndexes ContentIndex;
short ContentType;
int64 ContentSize;
byte Hash[0x20];
} ContentChunkRecords[Header.ContentCount];
} TMD;
Assert(sizeof(TMD) == Header.TMDFileSize);
byte Padding4[(64 - FTell() % 64) % 64];
} CIA;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment