mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Improved AGBSAVE / CMAC handling code
This commit is contained in:
parent
88a390f7a8
commit
e1b135ddc5
@ -2,55 +2,91 @@
|
||||
#include "sha.h"
|
||||
#include "aes.h"
|
||||
|
||||
u32 GetAgbSaveSize(u32 nand_src) {
|
||||
AgbSave* agbsave = (AgbSave*) NAND_BUFFER;
|
||||
NandPartitionInfo info;
|
||||
if ((GetNandPartitionInfo(&info, NP_TYPE_AGB, NP_SUBTYPE_CTR, 0, nand_src) != 0) ||
|
||||
(ReadNandSectors((u8*) agbsave, info.sector, 1, info.keyslot, nand_src) != 0))
|
||||
|
||||
u32 ValidateAgbSaveHeader(AgbSaveHeader* header) {
|
||||
u8 magic[] = { AGBSAVE_MAGIC };
|
||||
|
||||
// basic checks
|
||||
if ((memcmp(header->magic, magic, sizeof(magic)) != 0) ||
|
||||
(header->unknown0 != 1) || (header->save_start != 0x200) ||
|
||||
(header->save_size > AGBSAVE_MAX_SSIZE))
|
||||
return 1;
|
||||
|
||||
// reserved area checks
|
||||
for (u32 i = 0; i < sizeof(header->reserved0); i++) if (header->reserved0[i] != 0xFF) return 1;
|
||||
for (u32 i = 0; i < sizeof(header->reserved1); i++) if (header->reserved1[i] != 0xFF) return 1;
|
||||
for (u32 i = 0; i < sizeof(header->reserved2); i++) if (header->reserved2[i] != 0xFF) return 1;
|
||||
for (u32 i = 0; i < sizeof(header->reserved3); i++) if (header->reserved3[i] != 0xFF) return 1;
|
||||
|
||||
// all fine if arriving here
|
||||
return 0;
|
||||
return agbsave->save_size; // it's recommended to also check the CMAC
|
||||
}
|
||||
|
||||
u32 LoadAgbSave(u32 nand_src, u8* agbsave, u32 max_size, NandPartitionInfo* info, bool header_only) {
|
||||
AgbSaveHeader* header = (AgbSaveHeader*) agbsave;
|
||||
|
||||
// need at least room for the header
|
||||
if (max_size < sizeof(AgbSaveHeader)) return 1;
|
||||
|
||||
// load the header
|
||||
if ((GetNandPartitionInfo(info, NP_TYPE_AGB, NP_SUBTYPE_CTR, 0, nand_src) != 0) ||
|
||||
(ReadNandSectors(agbsave, info->sector, 1, info->keyslot, nand_src) != 0) ||
|
||||
(ValidateAgbSaveHeader(header) != 0) ||
|
||||
(sizeof(AgbSaveHeader) + header->save_size > info->count * 0x200))
|
||||
return 1;
|
||||
|
||||
// done if we only want the header
|
||||
if (header_only) return 0;
|
||||
|
||||
// load the savegame
|
||||
if ((sizeof(AgbSaveHeader) + header->save_size > max_size) ||
|
||||
(ReadNandBytes(agbsave + 0x200, (info->sector+1) * 0x200, header->save_size, info->keyslot, nand_src) != 0))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GetAgbSaveSize(u32 nand_src) {
|
||||
AgbSaveHeader* header = (AgbSaveHeader*) NAND_BUFFER;
|
||||
NandPartitionInfo info;
|
||||
if (LoadAgbSave(nand_src, NAND_BUFFER, NAND_BUFFER_SIZE, &info, true) != 0)
|
||||
return 1;
|
||||
return header->save_size; // it's recommended to also check the CMAC
|
||||
}
|
||||
|
||||
u32 CheckAgbSaveCmac(u32 nand_src) {
|
||||
u8 magic[] = { AGBSAVE_MAGIC };
|
||||
|
||||
AgbSave* agbsave = (AgbSave*) NAND_BUFFER;
|
||||
u8* agbsave = (u8*) NAND_BUFFER;
|
||||
AgbSaveHeader* header = (AgbSaveHeader*) agbsave;
|
||||
NandPartitionInfo info;
|
||||
if ((GetNandPartitionInfo(&info, NP_TYPE_AGB, NP_SUBTYPE_CTR, 0, nand_src) != 0) ||
|
||||
(ReadNandSectors((u8*) agbsave, info.sector, 1, info.keyslot, nand_src) != 0) ||
|
||||
(memcmp(agbsave->magic, magic, sizeof(magic)) != 0) ||
|
||||
(0x200 + agbsave->save_size > info.count * 0x200) ||
|
||||
(ReadNandBytes(agbsave->savegame, (info.sector+1) * 0x200, agbsave->save_size, info.keyslot, nand_src) != 0))
|
||||
if (LoadAgbSave(nand_src, agbsave, NAND_BUFFER_SIZE, &info, false) != 0)
|
||||
return 1;
|
||||
|
||||
u8 cmac[16] __attribute__((aligned(32)));
|
||||
u8 shasum[32];
|
||||
sha_quick(shasum, ((u8*) agbsave) + 0x30, (0x200 - 0x30) + agbsave->save_size, SHA256_MODE);
|
||||
sha_quick(shasum, agbsave + 0x30, (0x200 - 0x30) + header->save_size, SHA256_MODE);
|
||||
use_aeskey(0x24);
|
||||
aes_cmac(shasum, cmac, 2);
|
||||
|
||||
return (memcmp(cmac, agbsave->cmac, 16) == 0) ? 0 : 1;
|
||||
return (memcmp(cmac, header->cmac, 16) == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
u32 FixAgbSaveCmac(u32 nand_dst) {
|
||||
AgbSave* agbsave = (AgbSave*) NAND_BUFFER;
|
||||
u8* agbsave = (u8*) NAND_BUFFER;
|
||||
AgbSaveHeader* header = (AgbSaveHeader*) agbsave;
|
||||
NandPartitionInfo info;
|
||||
if ((GetNandPartitionInfo(&info, NP_TYPE_AGB, NP_SUBTYPE_CTR, 0, nand_dst) != 0) ||
|
||||
(ReadNandSectors((u8*) agbsave, info.sector, 1, info.keyslot, nand_dst) != 0) ||
|
||||
(0x200 + agbsave->save_size > info.count * 0x200) ||
|
||||
(ReadNandBytes(agbsave->savegame, (info.sector+1) * 0x200, agbsave->save_size, info.keyslot, nand_dst) != 0))
|
||||
if (LoadAgbSave(nand_dst, agbsave, NAND_BUFFER_SIZE, &info, false) != 0)
|
||||
return 1;
|
||||
|
||||
u8 cmac[16] __attribute__((aligned(32)));
|
||||
u8 shasum[32];
|
||||
sha_quick(shasum, ((u8*) agbsave) + 0x30, (0x200 - 0x30) + agbsave->save_size, SHA256_MODE);
|
||||
sha_quick(shasum, agbsave + 0x30, (0x200 - 0x30) + header->save_size, SHA256_MODE);
|
||||
use_aeskey(0x24);
|
||||
aes_cmac(shasum, cmac, 2);
|
||||
memcpy(agbsave->cmac, cmac, 16);
|
||||
memcpy(header->cmac, cmac, 16);
|
||||
|
||||
// set CFG_BOOTENV = 0x7 so the save is taken over
|
||||
// set CFG_BOOTENV to 0x7 so the save is taken over
|
||||
// https://www.3dbrew.org/wiki/CONFIG_Registers#CFG_BOOTENV
|
||||
*(u32*) 0x10010000 = 0x7;
|
||||
|
||||
return (WriteNandSectors((u8*) agbsave, info.sector, 1, info.keyslot, nand_dst) == 0) ? 0 : 1;
|
||||
return (WriteNandSectors(header, info.sector, 1, info.keyslot, nand_dst) == 0) ? 0 : 1;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "nand.h"
|
||||
|
||||
#define AGBSAVE_MAGIC '.', 'S', 'A', 'V'
|
||||
#define AGBSAVE_MAX_SIZE (0x000180 * 0x200) // standard size of the NAND partition
|
||||
#define AGBSAVE_MAX_SSIZE (AGBSAVE_MAX_SIZE - sizeof(AgbSaveHeader))
|
||||
|
||||
// see: http://3dbrew.org/wiki/3DS_Virtual_Console#NAND_Savegame
|
||||
typedef struct {
|
||||
@ -21,9 +23,9 @@ typedef struct {
|
||||
u32 unknown1; // has to do with ARM7?
|
||||
u32 unknown2; // has to do with ARM7?
|
||||
u8 reserved3[0x198]; // always 0xFF
|
||||
u8 savegame[(0x000180-1)*0x200]; // unknown on custom partitions
|
||||
} __attribute__((packed)) AgbSave;
|
||||
} __attribute__((packed)) AgbSaveHeader;
|
||||
|
||||
u32 ValidateAgbSaveHeader(AgbSaveHeader* header);
|
||||
u32 GetAgbSaveSize(u32 nand_src);
|
||||
u32 CheckAgbSaveCmac(u32 nand_src);
|
||||
u32 FixAgbSaveCmac(u32 nand_dst);
|
||||
|
@ -130,13 +130,13 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
|
||||
if (!cmac_type) { // path independent stuff
|
||||
const char* db_names[] = { SYS_DB_NAMES };
|
||||
for (sid = 0; sid < sizeof(db_names) / sizeof(char*); sid++)
|
||||
if (strncmp(name, db_names[sid], 16) == 0) break;
|
||||
if (strncasecmp(name, db_names[sid], 16) == 0) break;
|
||||
if (sid < sizeof(db_names) / sizeof(char*))
|
||||
cmac_type = ((drv == 'A') || (drv == 'B')) ? CMAC_TITLEDB_SD : CMAC_TITLEDB_SYS;
|
||||
else if (strncmp(name, "movable.sed", 16) == 0)
|
||||
else if (strncasecmp(name, "movable.sed", 16) == 0)
|
||||
cmac_type = CMAC_MOVABLE;
|
||||
/* else if (strncmp(name, "agbsave.bin", 16) == 0)
|
||||
cmac_type = CMAC_AGBSAVE; */
|
||||
else if (strncasecmp(name, "agbsave.bin", 16) == 0)
|
||||
cmac_type = CMAC_AGBSAVE;
|
||||
}
|
||||
|
||||
// exit with cmac_type if (u8*) cmac is NULL
|
||||
@ -164,9 +164,9 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
|
||||
// build hash data block, get size
|
||||
if (cmac_type == CMAC_AGBSAVE) { // agbsaves
|
||||
// see: https://www.3dbrew.org/wiki/Savegames#AES_CMAC_header
|
||||
AgbSave* agbsave = (AgbSave*) (void*) data;
|
||||
if ((fvx_qread(path, agbsave, 0, TEMP_BUFFER_SIZE, &br) != FR_OK) ||
|
||||
(br >= TEMP_BUFFER_SIZE) || (0x200 + agbsave->save_size > br))
|
||||
AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) data;
|
||||
if ((TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) || (fvx_qread(path, agbsave, 0, AGBSAVE_MAX_SIZE, &br) != FR_OK) ||
|
||||
(br < 0x200) || (ValidateAgbSaveHeader(agbsave) != 0) || (0x200 + agbsave->save_size > br))
|
||||
return 1;
|
||||
hashsize = (0x200 - 0x30) + agbsave->save_size;
|
||||
hashdata = data + 0x30;
|
||||
|
Loading…
x
Reference in New Issue
Block a user