2017-01-30 20:28:49 +01:00
|
|
|
#include "boss.h"
|
|
|
|
#include "sha.h"
|
|
|
|
#include "aes.h"
|
|
|
|
|
|
|
|
// http://3dbrew.org/wiki/SpotPass#Content_Header
|
|
|
|
u32 CheckBossHash(BossHeader* boss, bool encrypted) {
|
2019-05-08 00:18:34 +02:00
|
|
|
u8 hash_area[0x14] __attribute__((aligned(4))) = { 0 };
|
2017-01-30 20:28:49 +01:00
|
|
|
u8 boss_sha256[0x20];
|
|
|
|
u8 l_sha256[0x20];
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
// calculate hash
|
|
|
|
memcpy(hash_area, ((u8*) boss) + 0x28, 0x12);
|
|
|
|
memcpy(boss_sha256, boss->hash_header, 0x20);
|
|
|
|
if (encrypted) {
|
|
|
|
CryptBoss(hash_area, 0x28, 0x12, boss);
|
|
|
|
CryptBoss(boss_sha256, 0x28 + 0x12, 0x20, boss);
|
|
|
|
}
|
|
|
|
sha_quick(l_sha256, hash_area, 0x14, SHA256_MODE);
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
return (memcmp(boss_sha256, l_sha256, 0x20) == 0) ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 ValidateBossHeader(BossHeader* header, u32 fsize) {
|
|
|
|
u8 boss_magic[] = { BOSS_MAGIC };
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
// base checks
|
|
|
|
if ((memcmp(header->magic, boss_magic, sizeof(boss_magic)) != 0) ||
|
|
|
|
(fsize && (fsize != getbe32(header->filesize))) ||
|
|
|
|
(getbe32(header->filesize) < sizeof(BossHeader)) ||
|
|
|
|
(getbe16(header->unknown0) != 0x0001) ||
|
|
|
|
(getbe16(header->cnthdr_hash_type) != 0x0002) ||
|
|
|
|
(getbe16(header->cnthdr_rsa_size) != 0x0002))
|
|
|
|
return 1;
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
// hash check
|
|
|
|
if ((CheckBossHash(header, false) != 0) &&
|
|
|
|
(CheckBossHash(header, true) != 0))
|
|
|
|
return 1;
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 GetBossPayloadHashHeader(u8* header, BossHeader* boss) {
|
|
|
|
memset(header, 0, BOSS_SIZE_PAYLOAD_HEADER);
|
|
|
|
memcpy(header, ((u8*) boss) + 0x15A, 0x1C);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 CheckBossEncrypted(BossHeader* boss) {
|
|
|
|
return CheckBossHash(boss, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// on the fly de-/encryptor for BOSS
|
2017-06-01 15:11:41 +02:00
|
|
|
u32 CryptBoss(void* data, u32 offset, u32 size, BossHeader* boss) {
|
2017-01-30 20:28:49 +01:00
|
|
|
// check data area (encrypted area starts @0x28)
|
|
|
|
if (offset + size < 0x28) return 0;
|
|
|
|
else if (offset < 0x28) {
|
2017-06-01 15:11:41 +02:00
|
|
|
data = ((u8*)data + 0x28 - offset);
|
2017-01-30 20:28:49 +01:00
|
|
|
size -= 0x28 - offset;
|
|
|
|
offset = 0x28;
|
|
|
|
}
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
// decrypt BOSS data
|
|
|
|
u8 ctr[16] = { 0 };
|
|
|
|
memcpy(ctr, boss->ctr12, 12);
|
|
|
|
ctr[15] = 0x01;
|
|
|
|
use_aeskey(0x38);
|
|
|
|
ctr_decrypt_byte(data, data, size, offset - 0x28, AES_CNT_CTRNAND_MODE, ctr);
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// on the fly de-/encryptor for BOSS - sequential
|
2017-06-01 15:11:41 +02:00
|
|
|
u32 CryptBossSequential(void* data, u32 offset, u32 size) {
|
2017-01-30 20:28:49 +01:00
|
|
|
// warning: this will only work for sequential processing
|
|
|
|
// unexpected results otherwise
|
|
|
|
static BossHeader boss = { 0 };
|
|
|
|
static BossHeader* bossptr = NULL;
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
// fetch boss header from data
|
|
|
|
if ((offset == 0) && (size >= sizeof(BossHeader))) {
|
|
|
|
bossptr = NULL;
|
|
|
|
memcpy(&boss, data, sizeof(BossHeader));
|
|
|
|
if (((CheckBossEncrypted(&boss) == 0) &&
|
|
|
|
(CryptBoss((u8*) &boss, 0, sizeof(BossHeader), &boss) != 0)) ||
|
|
|
|
(ValidateBossHeader(&boss, 0) != 0))
|
|
|
|
return 1;
|
|
|
|
bossptr = &boss;
|
|
|
|
}
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
// safety check, boss pointer
|
|
|
|
if (!bossptr) return 1;
|
2020-08-24 21:27:19 -07:00
|
|
|
|
2017-01-30 20:28:49 +01:00
|
|
|
return CryptBoss(data, offset, size, bossptr);
|
|
|
|
}
|