mirror of
https://github.com/isfshax/isfshax.git
synced 2024-11-13 23:25:06 +01:00
259 lines
7.3 KiB
C
259 lines
7.3 KiB
C
|
#include "types.h"
|
||
|
#include "isfs/isfs.h"
|
||
|
#include "isfs/volume.h"
|
||
|
#include "nand.h"
|
||
|
|
||
|
#include "crypto.h"
|
||
|
#include "aes.h"
|
||
|
#include "hmac.h"
|
||
|
#include "string.h"
|
||
|
|
||
|
static u8 slc_super_buf[ISFSSUPER_SIZE];
|
||
|
/*static u8 slccmpt_super_buf[ISFSSUPER_SIZE];*/
|
||
|
|
||
|
isfs_ctx isfs[4] = {
|
||
|
[ISFSVOL_SLC]
|
||
|
{
|
||
|
.volume = ISFSVOL_SLC,
|
||
|
.name = "slc",
|
||
|
.bank = BANK_SLC,
|
||
|
.key = &otp.nand_key,
|
||
|
.hmac = &otp.nand_hmac,
|
||
|
.super_count = 64,
|
||
|
.super = slc_super_buf,
|
||
|
},
|
||
|
/*[ISFSVOL_SLCCMPT]
|
||
|
{
|
||
|
.volume = ISFSVOL_SLCCMPT,
|
||
|
.name = "slccmpt",
|
||
|
.bank = BANK_SLCCMPT,
|
||
|
.key = &otp.wii_nand_key,
|
||
|
.hmac = &otp.wii_nand_hmac,
|
||
|
.super_count = 16,
|
||
|
.super = slccmpt_super_buf,
|
||
|
},*/
|
||
|
};
|
||
|
|
||
|
int isfs_num_volumes(void)
|
||
|
{
|
||
|
return sizeof(isfs) / sizeof(isfs_ctx);
|
||
|
}
|
||
|
|
||
|
isfs_ctx* isfs_get_volume(int volume)
|
||
|
{
|
||
|
if(volume < isfs_num_volumes() && volume >= 0)
|
||
|
return &isfs[volume];
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
char* isfs_do_volume(const char* path, isfs_ctx** ctx)
|
||
|
{
|
||
|
isfs_ctx* volume = NULL;
|
||
|
|
||
|
if(!path) return NULL;
|
||
|
const char* filename = strchr(path, ':');
|
||
|
|
||
|
if(!filename) return NULL;
|
||
|
if(filename[1] != '/') return NULL;
|
||
|
|
||
|
char mount[sizeof(volume->name)] = {0};
|
||
|
memcpy(mount, path, filename - path);
|
||
|
|
||
|
for(int i = 0; i < isfs_num_volumes(); i++)
|
||
|
{
|
||
|
volume = &isfs[i];
|
||
|
if(strcmp(mount, volume->name)) continue;
|
||
|
|
||
|
if(!volume->mounted) return NULL;
|
||
|
|
||
|
*ctx = volume;
|
||
|
return (char*)(filename + 1);
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
int isfs_read_volume(const isfs_ctx* ctx, u32 start_cluster, u32 cluster_count, u32 flags, void *hmac_seed, void *data)
|
||
|
{
|
||
|
u8 saved_hmacs[2][20] = {0}, hmac[20] = {0};
|
||
|
int rc = ISFSVOL_OK;
|
||
|
u32 i, p;
|
||
|
|
||
|
/* enable slc or slccmpt bank */
|
||
|
nand_enable_banks(ctx->bank);
|
||
|
|
||
|
/* read all requested clusters */
|
||
|
for (i = 0; i < cluster_count; i++)
|
||
|
{
|
||
|
u32 cluster = start_cluster + i;
|
||
|
u8 *cluster_data = (u8 *)data + i * CLUSTER_SIZE;
|
||
|
u32 cluster_start = cluster * CLUSTER_PAGES;
|
||
|
|
||
|
/* read cluster pages */
|
||
|
for (p = 0; p < CLUSTER_PAGES; p++)
|
||
|
{
|
||
|
u8 spare[SPARE_SIZE] = {0};
|
||
|
|
||
|
/* attempt to read the page (and correct ecc errors) */
|
||
|
rc = nand_read_page(cluster_start + p, &cluster_data[p * PAGE_SIZE], spare);
|
||
|
|
||
|
/* uncorrectable ecc error or other issues */
|
||
|
if (rc < 0)
|
||
|
return ISFSVOL_ERROR_READ;
|
||
|
|
||
|
/* ECC errors, a refresh might be needed */
|
||
|
if (rc > 0)
|
||
|
rc = ISFSVOL_ECC_CORRECTED;
|
||
|
|
||
|
/* page 6 and 7 store the hmac */
|
||
|
if (p == 6)
|
||
|
{
|
||
|
memcpy(saved_hmacs[0], &spare[1], 20);
|
||
|
memcpy(saved_hmacs[1], &spare[21], 12);
|
||
|
}
|
||
|
if (p == 7)
|
||
|
memcpy(&saved_hmacs[1][12], &spare[1], 8);
|
||
|
}
|
||
|
|
||
|
/* decrypt cluster */
|
||
|
if (flags & ISFSVOL_FLAG_ENCRYPTED)
|
||
|
{
|
||
|
aes_reset();
|
||
|
aes_set_key(ctx->key);
|
||
|
aes_empty_iv();
|
||
|
aes_decrypt(cluster_data, cluster_data, CLUSTER_SIZE / AES_BLOCK_SIZE, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* verify hmac */
|
||
|
if (flags & ISFSVOL_FLAG_HMAC)
|
||
|
{
|
||
|
hmac_ctx calc_hmac;
|
||
|
int matched = 0;
|
||
|
|
||
|
/* compute clusters hmac */
|
||
|
hmac_init(&calc_hmac, ctx->hmac, 20);
|
||
|
hmac_update(&calc_hmac, (const u8 *)hmac_seed, SHA_BLOCK_SIZE);
|
||
|
hmac_update(&calc_hmac, (const u8 *)data, cluster_count * CLUSTER_SIZE);
|
||
|
hmac_final(&calc_hmac, hmac);
|
||
|
|
||
|
/* ensure at least one of the saved hmacs matches */
|
||
|
matched += !memcmp(saved_hmacs[0], hmac, sizeof(hmac));
|
||
|
matched += !memcmp(saved_hmacs[1], hmac, sizeof(hmac));
|
||
|
|
||
|
if (matched == 2)
|
||
|
rc = ISFSVOL_OK;
|
||
|
else if (matched == 1)
|
||
|
rc = ISFSVOL_HMAC_PARTIAL;
|
||
|
else
|
||
|
rc = ISFSVOL_ERROR_HMAC;
|
||
|
}
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
int isfs_write_volume(const isfs_ctx* ctx, u32 start_cluster, u32 cluster_count, u32 flags, void *hmac_seed, void *data)
|
||
|
{
|
||
|
static u8 blockpg[64][PAGE_SIZE] ALIGNED(64), blocksp[64][SPARE_SIZE];
|
||
|
static u8 pgbuf[PAGE_SIZE] ALIGNED(64), spbuf[SPARE_SIZE];
|
||
|
u8 hmac[20] = {0};
|
||
|
int rc = ISFSVOL_OK;
|
||
|
u32 b, p;
|
||
|
|
||
|
/* enable slc or slccmpt bank */
|
||
|
nand_enable_banks(ctx->bank);
|
||
|
|
||
|
/* compute clusters hmac */
|
||
|
if (flags & ISFSVOL_FLAG_HMAC)
|
||
|
{
|
||
|
hmac_ctx calc_hmac;
|
||
|
hmac_init(&calc_hmac, ctx->hmac, 20);
|
||
|
hmac_update(&calc_hmac, (const u8 *)hmac_seed, SHA_BLOCK_SIZE);
|
||
|
hmac_update(&calc_hmac, (const u8 *)data, cluster_count * CLUSTER_SIZE);
|
||
|
hmac_final(&calc_hmac, hmac);
|
||
|
}
|
||
|
|
||
|
/* setup clusters encryption */
|
||
|
if (flags & ISFSVOL_FLAG_ENCRYPTED)
|
||
|
{
|
||
|
aes_reset();
|
||
|
aes_set_key(ctx->key);
|
||
|
aes_empty_iv();
|
||
|
}
|
||
|
|
||
|
u32 startpage = start_cluster * CLUSTER_PAGES;
|
||
|
u32 endpage = (start_cluster + cluster_count) * CLUSTER_PAGES;
|
||
|
|
||
|
u32 startblock = start_cluster / BLOCK_CLUSTERS;
|
||
|
u32 endblock = (start_cluster + cluster_count + BLOCK_CLUSTERS - 1) / BLOCK_CLUSTERS;
|
||
|
|
||
|
/* process data in nand blocks */
|
||
|
for (b = startblock; (b < endblock) && (rc >= 0); b++)
|
||
|
{
|
||
|
u32 firstblockpage = b * BLOCK_PAGES;
|
||
|
|
||
|
/* prepare block */
|
||
|
for (p = 0; p < 64; p++)
|
||
|
{
|
||
|
u32 curpage = firstblockpage + p; /* current page */
|
||
|
u32 clusidx = curpage % CLUSTER_PAGES; /* index in cluster */
|
||
|
|
||
|
/* if this page is unmodified, read it from nand */
|
||
|
if ((curpage < startpage) || (curpage >= endpage))
|
||
|
{
|
||
|
if (nand_read_page(curpage, blockpg[p], blocksp[p]) < 0)
|
||
|
return ISFSVOL_ERROR_READ;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* place hmac in page 6 and 7 of a cluster */
|
||
|
memset(blocksp[p], 0, SPARE_SIZE);
|
||
|
switch (clusidx)
|
||
|
{
|
||
|
case 6:
|
||
|
memcpy(&blocksp[p][1], hmac, 20);
|
||
|
memcpy(&blocksp[p][21], hmac, 12);
|
||
|
break;
|
||
|
case 7:
|
||
|
memcpy(&blocksp[p][1], &hmac[12], 8);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* encrypt or copy the data */
|
||
|
u8 *srcdata = (u8*)data + (curpage - startpage) * PAGE_SIZE;
|
||
|
if (flags & ISFSVOL_FLAG_ENCRYPTED)
|
||
|
aes_encrypt(blockpg[p], srcdata, PAGE_SIZE / AES_BLOCK_SIZE, clusidx > 0);
|
||
|
else
|
||
|
memcpy(blockpg[p], srcdata, PAGE_SIZE);
|
||
|
}
|
||
|
|
||
|
/* erase block */
|
||
|
if (nand_erase_block(b) < 0)
|
||
|
return ISFSVOL_ERROR_ERASE;
|
||
|
|
||
|
/* write block */
|
||
|
for (p = 0; p < BLOCK_PAGES; p++)
|
||
|
if (nand_write_page(firstblockpage + p, blockpg[p], blocksp[p]) < 0)
|
||
|
rc = ISFSVOL_ERROR_WRITE;
|
||
|
|
||
|
/* check if pages should be verified after writing */
|
||
|
if (rc || !(flags & ISFSVOL_FLAG_READBACK))
|
||
|
continue;
|
||
|
|
||
|
/* read back pages */
|
||
|
for (p = 0; p < BLOCK_PAGES; p++)
|
||
|
{
|
||
|
if (nand_read_page(firstblockpage + p, pgbuf, spbuf) < 0)
|
||
|
return ISFSVOL_ERROR_READ;
|
||
|
|
||
|
/* page content doesn't match */
|
||
|
if (memcmp(blockpg[p], pgbuf, PAGE_SIZE) ||
|
||
|
memcmp(&blocksp[p][1], &spbuf[1], 0x20))
|
||
|
return ISFSVOL_ERROR_READBACK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return rc;
|
||
|
}
|