forked from Mirror/GodMode9
Except for cia building or loading cia just yet. Added more checks on ticket content index, mainly due to having effects in the ticket format itself, and are unknown still. Ability to determine ticket size. Verify signature with ticket's proper size. Changes to use the new Ticket struct with the flexible array member.
131 lines
4.8 KiB
C
131 lines
4.8 KiB
C
#include "cia.h"
|
|
#include "ncch.h"
|
|
#include "unittype.h"
|
|
#include "ff.h"
|
|
#include "aes.h"
|
|
#include "sha.h"
|
|
|
|
u32 ValidateCiaHeader(CiaHeader* header) {
|
|
if ((header->size_header != CIA_HEADER_SIZE) ||
|
|
(header->size_cert != CIA_CERT_SIZE) ||
|
|
(header->size_ticket != TICKET_COMMON_SIZE) ||
|
|
(header->size_tmd < TMD_SIZE_MIN) ||
|
|
(header->size_tmd > TMD_SIZE_MAX) ||
|
|
(header->size_content == 0))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
u32 GetCiaInfo(CiaInfo* info, CiaHeader* header) {
|
|
if ((u8*) info != (u8*) header) memcpy(info, header, 0x20); // take over first 0x20 byte
|
|
|
|
info->offset_cert = align(header->size_header, 64);
|
|
info->offset_ticket = info->offset_cert + align(header->size_cert, 64);
|
|
info->offset_tmd = info->offset_ticket + align(header->size_ticket, 64);
|
|
info->offset_content = info->offset_tmd + align(header->size_tmd, 64);
|
|
info->offset_meta = (header->size_meta) ? info->offset_content + align(header->size_content, 64) : 0;
|
|
info->offset_content_list = info->offset_tmd + sizeof(TitleMetaData);
|
|
|
|
info->size_content_list = info->size_tmd - sizeof(TitleMetaData);
|
|
info->size_cia = (header->size_meta) ? info->offset_meta + info->size_meta :
|
|
info->offset_content + info->size_content;
|
|
|
|
info->max_contents = (info->size_tmd - sizeof(TitleMetaData)) / sizeof(TmdContentChunk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 FixCiaHeaderForTmd(CiaHeader* header, TitleMetaData* tmd) {
|
|
TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1);
|
|
u32 content_count = getbe16(tmd->content_count);
|
|
header->size_content = 0;
|
|
header->size_tmd = TMD_SIZE_N(content_count);
|
|
memset(header->content_index, 0, sizeof(header->content_index));
|
|
for (u32 i = 0; i < content_count; i++) {
|
|
u16 index = getbe16(content_list[i].index);
|
|
header->size_content += getbe64(content_list[i].size);
|
|
header->content_index[index/8] |= (1 << (7-(index%8)));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 BuildCiaCert(u8* ciacert) {
|
|
const u8 cert_hash_expected[0x20] = {
|
|
0xC7, 0x2E, 0x1C, 0xA5, 0x61, 0xDC, 0x9B, 0xC8, 0x05, 0x58, 0x58, 0x9C, 0x63, 0x08, 0x1C, 0x8A,
|
|
0x10, 0x78, 0xDF, 0x42, 0x99, 0x80, 0x3A, 0x68, 0x58, 0xF0, 0x41, 0xF9, 0xCB, 0x10, 0xE6, 0x35
|
|
};
|
|
const u8 cert_hash_expected_dev[0x20] = {
|
|
0xFB, 0xD2, 0xC0, 0x47, 0x95, 0xB9, 0x4C, 0xC8, 0x0B, 0x64, 0x58, 0x96, 0xF6, 0x61, 0x0F, 0x52,
|
|
0x18, 0x83, 0xAF, 0xE0, 0xF4, 0xE5, 0x62, 0xBA, 0x69, 0xEE, 0x72, 0x2A, 0xC2, 0x4E, 0x95, 0xB3
|
|
};
|
|
|
|
// open certs.db file on SysNAND
|
|
FIL db;
|
|
UINT bytes_read;
|
|
if (f_open(&db, "1:/dbs/certs.db", FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
|
return 1;
|
|
// grab CIA cert from 4 offsets
|
|
f_lseek(&db, 0x0C10);
|
|
f_read(&db, ciacert + 0x000, 0x1F0, &bytes_read);
|
|
f_lseek(&db, 0x3A00);
|
|
f_read(&db, ciacert + 0x1F0, 0x210, &bytes_read);
|
|
f_lseek(&db, 0x3F10);
|
|
f_read(&db, ciacert + 0x400, 0x300, &bytes_read);
|
|
f_lseek(&db, 0x3C10);
|
|
f_read(&db, ciacert + 0x700, 0x300, &bytes_read);
|
|
f_close(&db);
|
|
|
|
// check the certificate hash
|
|
u8 cert_hash[0x20];
|
|
sha_quick(cert_hash, ciacert, CIA_CERT_SIZE, SHA256_MODE);
|
|
if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 BuildCiaMeta(CiaMeta* meta, void* exthdr, void* smdh) {
|
|
// init metadata with all zeroes and core version
|
|
memset(meta, 0x00, sizeof(CiaMeta));
|
|
meta->core_version = 2;
|
|
// copy dependencies from extheader
|
|
if (exthdr) memcpy(meta->dependencies, ((NcchExtHeader*) exthdr)->dependencies, sizeof(meta->dependencies));
|
|
// copy smdh (icon file in exefs)
|
|
if (smdh) memcpy(meta->smdh, smdh, sizeof(meta->smdh));
|
|
return 0;
|
|
}
|
|
|
|
u32 BuildCiaHeader(CiaHeader* header, u32 ticket_size) {
|
|
memset(header, 0, sizeof(CiaHeader));
|
|
// sizes in header - fill only known sizes, others zero
|
|
header->size_header = sizeof(CiaHeader);
|
|
header->size_cert = CIA_CERT_SIZE;
|
|
header->size_ticket = ticket_size;
|
|
header->size_tmd = 0;
|
|
header->size_meta = 0;
|
|
header->size_content = 0;
|
|
return 0;
|
|
}
|
|
|
|
u32 DecryptCiaContentSequential(void* data, u32 size, u8* ctr, const u8* titlekey) {
|
|
// WARNING: size and offset of data have to be a multiple of 16
|
|
u8 tik[16] __attribute__((aligned(32)));
|
|
u32 mode = AES_CNT_TITLEKEY_DECRYPT_MODE;
|
|
memcpy(tik, titlekey, 16);
|
|
setup_aeskey(0x11, tik);
|
|
use_aeskey(0x11);
|
|
cbc_decrypt(data, data, size / 16, mode, ctr);
|
|
return 0;
|
|
}
|
|
|
|
u32 EncryptCiaContentSequential(void* data, u32 size, u8* ctr, const u8* titlekey) {
|
|
// WARNING: size and offset of data have to be a multiple of 16
|
|
u8 tik[16] __attribute__((aligned(32)));
|
|
u32 mode = AES_CNT_TITLEKEY_ENCRYPT_MODE;
|
|
memcpy(tik, titlekey, 16);
|
|
setup_aeskey(0x11, tik);
|
|
use_aeskey(0x11);
|
|
cbc_encrypt(data, data, size / 16, mode, ctr);
|
|
return 0;
|
|
}
|