#pragma once #include "common.h" #include "tmd.h" #define TICKET_COMMON_SIZE sizeof(TicketCommon) #define TICKET_MINIMUM_SIZE sizeof(TicketMinimum) #define TICKET_TWL_SIZE sizeof(Ticket) #define TICKET_CDNCERT_SIZE 0x700 #define TICKET_MAX_CONTENTS TITLE_MAX_CONTENTS // should be TMD_MAX_CONTENTS #define TICKET_COMMON_CNT_INDEX_SIZE (0x28 + (((TICKET_MAX_CONTENTS + 1023) >> 10) * 0x84)) #define TICKET_ISSUER "Root-CA00000003-XS0000000c" #define TICKET_ISSUER_DEV "Root-CA00000004-XS00000009" #define TICKET_SIG_TYPE 0x00, 0x01, 0x00, 0x04 // RSA_2048 SHA256 #define TICKET_ISSUER_TWL "Root-CA00000001-XS00000006" #define TICKET_SIG_TYPE_TWL 0x00, 0x01, 0x00, 0x01 // RSA_2048 SHA1 #define TICKET_DEVKIT(tick) (strncmp((char*)tick->issuer, TICKET_ISSUER_DEV, 0x40) == 0) // from: https://github.com/profi200/Project_CTR/blob/02159e17ee225de3f7c46ca195ff0f9ba3b3d3e4/ctrtool/tik.h#L15-L39 // all numbers in big endian #define TICKETBASE \ u8 sig_type[4]; \ u8 signature[0x100]; \ u8 padding1[0x3C]; \ u8 issuer[0x40]; \ u8 ecdsa[0x3C]; \ u8 version; \ u8 ca_crl_version; \ u8 signer_crl_version; \ u8 titlekey[0x10]; \ u8 reserved0; \ u8 ticket_id[8]; \ u8 console_id[4]; \ u8 title_id[8]; \ u8 sys_access[2]; \ u8 ticket_version[2]; \ u8 time_mask[4]; \ u8 permit_mask[4]; \ u8 title_export; \ u8 commonkey_idx; \ u8 reserved1[0x2A]; \ u8 eshop_id[4]; \ u8 reserved2; \ u8 audit; \ u8 content_permissions[0x40]; \ u8 reserved3[2]; \ u8 timelimits[0x40] typedef struct { TICKETBASE; u8 content_index[]; } PACKED_STRUCT Ticket; typedef struct { TICKETBASE; u8 content_index[TICKET_COMMON_CNT_INDEX_SIZE]; } PACKED_STRUCT TicketCommon; // minimum allowed content_index is 0x14 typedef struct { TICKETBASE; u8 content_index[0x14]; } PACKED_STRUCT TicketMinimum; typedef struct { u8 unk1[2]; u8 unk2[2]; u8 content_index_size[4]; u8 data_header_relative_offset[4]; // relative to content index start u8 unk3[2]; u8 unk4[2]; u8 unk5[4]; } PACKED_ALIGN(1) TicketContentIndexMainHeader; typedef struct { u8 data_relative_offset[4]; // relative to content index start u8 max_entry_count[4]; u8 size_per_entry[4]; // but has no effect u8 total_size_used[4]; // also no effect u8 data_type[2]; // perhaps, does have effect and change with different data like on 0004000D tickets u8 unknown[2]; // or padding } PACKED_ALIGN(1) TicketContentIndexDataHeader; // data type == 3 typedef struct { u8 unk[2]; // seemly has no meaning u8 indexoffset[2]; u8 rightsbitfield[0x80]; } PACKED_ALIGN(1) TicketRightsField; typedef struct { size_t count; const TicketRightsField* rights; // points within ticket pointer } TicketRightsCheck; u32 ValidateTicket(Ticket* ticket); u32 ValidateTwlTicket(Ticket* ticket); u32 ValidateTicketSignature(Ticket* ticket); u32 BuildFakeTicket(Ticket* ticket, const u8* title_id); u32 GetTicketContentIndexSize(const Ticket* ticket); u32 GetTicketSize(const Ticket* ticket); u32 BuildTicketCert(u8* tickcert); u32 TicketRightsCheck_InitContext(TicketRightsCheck* ctx, Ticket* ticket); bool TicketRightsCheck_CheckIndex(TicketRightsCheck* ctx, u16 index);