160 lines
6.0 KiB
C
Raw Normal View History

#include "cia.h"
2016-12-15 11:46:00 +01:00
#include "ncch.h"
2017-02-26 14:04:03 +01:00
#include "unittype.h"
#include "ff.h"
#include "aes.h"
#include "sha.h"
#include "cert.h"
u32 ValidateCiaHeader(CiaHeader* header) {
if ((header->size_header != CIA_HEADER_SIZE) ||
(header->size_cert != CIA_CERT_SIZE) ||
(header->size_ticket != TICKET_COMMON_SIZE) ||
2016-12-19 13:50:03 +01:00
(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;
}
2016-12-15 11:46:00 +01:00
u32 FixCiaHeaderForTmd(CiaHeader* header, TitleMetaData* tmd) {
TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1);
u32 content_count = getbe16(tmd->content_count);
header->size_content = 0;
2016-12-19 13:50:03 +01:00
header->size_tmd = TMD_SIZE_N(content_count);
2016-12-15 11:46:00 +01:00
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) {
static 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
};
static const u8 cert_hash_expected_dev[0x20] = {
2017-02-26 14:04:03 +01:00
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
};
const char* issuer_ca = !IS_DEVKIT ? "Root-CA00000003" : "Root-CA00000004";
const char* issuer_xs = !IS_DEVKIT ? "Root-CA00000003-XS0000000c" : "Root-CA00000004-XS00000009";
const char* issuer_cp = !IS_DEVKIT ? "Root-CA00000003-CP0000000b" : "Root-CA00000004-CP0000000a";
// open certs.db file on SysNAND or EmuNAND
Certificate cert_ca;
Certificate cert_xs;
Certificate cert_cp;
if (LoadCertFromCertDb(false, &cert_ca, issuer_ca) != 0 && LoadCertFromCertDb(true, &cert_ca, issuer_ca) != 0)
return 1;
if (LoadCertFromCertDb(false, &cert_xs, issuer_xs) != 0 && LoadCertFromCertDb(true, &cert_xs, issuer_xs) != 0) {
Certificate_Cleanup(&cert_ca);
return 1;
}
if (LoadCertFromCertDb(false, &cert_cp, issuer_cp) != 0 && LoadCertFromCertDb(true, &cert_cp, issuer_cp) != 0) {
Certificate_Cleanup(&cert_ca);
Certificate_Cleanup(&cert_xs);
return 1;
}
u32 cert_size_ca;
u32 cert_size_xs;
u32 cert_size_cp;
if (Certificate_GetFullSize(&cert_ca, &cert_size_ca) != 0 ||
cert_size_ca != 0x400 ||
Certificate_GetFullSize(&cert_xs, &cert_size_xs) != 0 ||
cert_size_xs != 0x300 ||
Certificate_GetFullSize(&cert_cp, &cert_size_cp) != 0 ||
cert_size_cp != 0x300 ||
Certificate_RawCopy(&cert_ca, ciacert) != 0 ||
Certificate_RawCopy(&cert_xs, &ciacert[0x400]) != 0 ||
Certificate_RawCopy(&cert_cp, &ciacert[0x700]) != 0) {
Certificate_Cleanup(&cert_ca);
Certificate_Cleanup(&cert_xs);
Certificate_Cleanup(&cert_cp);
return 1;
}
Certificate_Cleanup(&cert_ca);
Certificate_Cleanup(&cert_xs);
Certificate_Cleanup(&cert_cp);
// check the certificate hash
u8 cert_hash[0x20];
sha_quick(cert_hash, ciacert, CIA_CERT_SIZE, SHA256_MODE);
2017-02-26 14:04:03 +01:00
if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0)
return 1;
return 0;
}
2016-12-19 01:33:30 +01:00
u32 BuildCiaMeta(CiaMeta* meta, void* exthdr, void* smdh) {
2016-12-15 11:46:00 +01:00
// init metadata with all zeroes and core version
memset(meta, 0x00, sizeof(CiaMeta));
meta->core_version = 2;
// copy dependencies from extheader
2016-12-19 01:33:30 +01:00
if (exthdr) memcpy(meta->dependencies, ((NcchExtHeader*) exthdr)->dependencies, sizeof(meta->dependencies));
2016-12-15 11:46:00 +01:00
// copy smdh (icon file in exefs)
if (smdh) memcpy(meta->smdh, smdh, sizeof(meta->smdh));
return 0;
}
u32 BuildCiaHeader(CiaHeader* header, u32 ticket_size) {
2016-12-15 11:46:00 +01:00
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;
2016-12-15 11:46:00 +01:00
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;
}
2016-12-15 11:46:00 +01:00
u32 EncryptCiaContentSequential(void* data, u32 size, u8* ctr, const u8* titlekey) {
2016-12-15 11:46:00 +01:00
// 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;
}