Handle SD data crypto

This commit is contained in:
d0k3 2016-10-30 15:57:54 +01:00
parent 779b6fe25b
commit 94c0608477
3 changed files with 75 additions and 51 deletions

View File

@ -6,6 +6,15 @@
#define SDCRYPT_BUFFER_SIZE (0x100000) #define SDCRYPT_BUFFER_SIZE (0x100000)
#define NUM_ALIAS_DRV 2 #define NUM_ALIAS_DRV 2
#define NUM_FILCRYPTINFO 16
typedef struct {
FIL* fptr;
u8 ctr[16];
u8 keyy[16];
} __attribute__((packed)) FilCryptInfo;
static FilCryptInfo filcrypt[NUM_FILCRYPTINFO] = { 0 };
char alias_drv[NUM_ALIAS_DRV]; // 1 char ASCII drive number of the alias drive / 0x00 if unused char alias_drv[NUM_ALIAS_DRV]; // 1 char ASCII drive number of the alias drive / 0x00 if unused
char alias_path[NUM_ALIAS_DRV][128]; // full path to resolve the alias into char alias_path[NUM_ALIAS_DRV][128]; // full path to resolve the alias into
@ -31,86 +40,107 @@ void dealias_path (TCHAR* alias, const TCHAR* path) {
else strncpy(alias, path, 256); else strncpy(alias, path, 256);
} }
void fx_crypt(XFIL* xfp, void* buff, FSIZE_t off, UINT bt) { FilCryptInfo* fx_find_cryptinfo(FIL* fptr) {
FilCryptInfo* info = NULL;
for (u32 i = 0; i < NUM_FILCRYPTINFO; i++) {
if (!info && !filcrypt[i].fptr) // use first free
info = &filcrypt[i];
if (fptr == filcrypt[i].fptr) {
info = &filcrypt[i];
break;
}
}
return info;
}
void fx_crypt(FilCryptInfo* info, void* buff, FSIZE_t off, UINT bt) {
u32 mode = AES_CNT_CTRNAND_MODE; u32 mode = AES_CNT_CTRNAND_MODE;
u8 ctr[16] __attribute__((aligned(32))); u8 ctr[16] __attribute__((aligned(32)));
u8 buff16[16]; u8 buff16[16];
u8* buffer = buff; u8* buffer = buff;
// copy CTR and increment it // copy CTR and increment it
memcpy(ctr, xfp->iv, 16); memcpy(ctr, info->ctr, 16);
add_ctr(ctr, off / 16); add_ctr(ctr, off / 16);
// setup the key // setup the key
setup_aeskeyY(xfp->keyslot, xfp->keyy); setup_aeskeyY(0x34, info->keyy);
use_aeskey(xfp->keyslot); use_aeskey(0x34);
// handle misaligned offset (at beginning) // handle misaligned offset (at beginning)
if (off % 16) { if (off % 16) {
memcpy(buff16 + (off % 16), buff, 16 - (off % 16)); memcpy(buff16 + (off % 16), buffer, 16 - (off % 16));
ctr_decrypt(buff16, buff16, 1, mode, ctr); ctr_decrypt(buff16, buff16, 1, mode, ctr);
bt -= 16 - (off % 16); memcpy(buffer, buff16 + (off % 16), 16 - (off % 16));
buffer += 16 - (off % 16); buffer += 16 - (off % 16);
bt -= 16 - (off % 16);
} }
// de/encrypt the data // de/encrypt the data
ctr_decrypt(buff, buff, bt / 16, mode, ctr); ctr_decrypt(buffer, buffer, bt / 16, mode, ctr);
bt -= 16 * (UINT) (bt / 16);
buffer += 16 * (UINT) (bt / 16); buffer += 16 * (UINT) (bt / 16);
bt -= 16 * (UINT) (bt / 16);
// handle misaligned offset (at end) // handle misaligned offset (at end)
if (bt) { if (bt) {
memcpy(buff16, buff, bt); memcpy(buff16, buffer, bt);
ctr_decrypt(buff16, buff16, 1, mode, ctr); ctr_decrypt(buff16, buff16, 1, mode, ctr);
memcpy(buffer, buff16, bt);
buffer += bt; buffer += bt;
bt = 0; bt = 0;
} }
} }
FRESULT fx_open (FIL* fp, XFIL* xfp, const TCHAR* path, BYTE mode) { FRESULT fx_open (FIL* fp, const TCHAR* path, BYTE mode) {
int num = alias_num(path); int num = alias_num(path);
xfp->keyslot = 0x40; FilCryptInfo* info = fx_find_cryptinfo(fp);
if (num >= 0) { if (info) info->fptr = NULL;
if (info && (num >= 0)) {
// get AES counter, see: http://www.3dbrew.org/wiki/Extdata#Encryption // get AES counter, see: http://www.3dbrew.org/wiki/Extdata#Encryption
// path is the part of the full path after //Nintendo 3DS/<ID0>/<ID1> // path is the part of the full path after //Nintendo 3DS/<ID0>/<ID1>
u8 hashstr[256]; u8 hashstr[256];
u8 sha256sum[32]; u8 sha256sum[32];
u32 plen = 0; u32 plen = 0;
// poor man's UTF-8 -> UTF-16 // poor man's UTF-8 -> UTF-16
for (u32 plen = 0; plen < 128; plen++) { for (plen = 0; plen < 128; plen++) {
hashstr[2*plen] = path[2 + plen]; hashstr[2*plen] = path[2 + plen];
hashstr[2*plen+1] = 0; hashstr[2*plen+1] = 0;
if (path[plen] == 0) break; if (path[2 + plen] == 0) break;
} }
sha_quick(sha256sum, hashstr, (plen + 1) * 2, SHA256_MODE); sha_quick(sha256sum, hashstr, (plen + 1) * 2, SHA256_MODE);
for (u32 i = 0; i < 16; i++) for (u32 i = 0; i < 16; i++)
xfp->iv[i] = sha256sum[i] ^ sha256sum[i+16]; info->ctr[i] = sha256sum[i] ^ sha256sum[i+16];
// copy over key, set keyslot // copy over key, FIL pointer
memcpy(xfp->keyy, sd_keyy[num], 16); memcpy(info->keyy, sd_keyy[num], 16);
xfp->keyslot = 0x34; info->fptr = fp;
} }
return fa_open(fp, path, mode); return fa_open(fp, path, mode);
} }
FRESULT fx_read (FIL* fp, XFIL* xfp, void* buff, UINT btr, UINT* br) { FRESULT fx_read (FIL* fp, void* buff, UINT btr, UINT* br) {
FilCryptInfo* info = fx_find_cryptinfo(fp);
FSIZE_t off = f_tell(fp); FSIZE_t off = f_tell(fp);
FRESULT res = f_read(fp, buff, btr, br); FRESULT res = f_read(fp, buff, btr, br);
if (xfp && (xfp->keyslot < 0x40)) if (info && info->fptr)
fx_crypt(xfp, buff, off, btr); fx_crypt(info, buff, off, btr);
return res; return res;
} }
FRESULT fx_write (FIL* fp, XFIL* xfp, const void* buff, UINT btw, UINT* bw) { FRESULT fx_write (FIL* fp, const void* buff, UINT btw, UINT* bw) {
FilCryptInfo* info = fx_find_cryptinfo(fp);
FSIZE_t off = f_tell(fp); FSIZE_t off = f_tell(fp);
FRESULT res = FR_OK; FRESULT res = FR_OK;
if (xfp && (xfp->keyslot < 0x40)) { if (info && info->fptr) {
*bw = 0; *bw = 0;
for (UINT p = 0; (p < btw) && (res == FR_OK); p += SDCRYPT_BUFFER_SIZE) { for (UINT p = 0; (p < btw) && (res == FR_OK); p += SDCRYPT_BUFFER_SIZE) {
UINT pcount = min(SDCRYPT_BUFFER_SIZE, (btw - p)); UINT pcount = min(SDCRYPT_BUFFER_SIZE, (btw - p));
UINT bwl = 0; UINT bwl = 0;
memcpy(SDCRYPT_BUFFER, (u8*) buff + p, pcount); memcpy(SDCRYPT_BUFFER, (u8*) buff + p, pcount);
fx_crypt(xfp, SDCRYPT_BUFFER, off + p, pcount); fx_crypt(info, SDCRYPT_BUFFER, off + p, pcount);
res = f_write(fp, (const void*) SDCRYPT_BUFFER, pcount, &bwl); res = f_write(fp, (const void*) SDCRYPT_BUFFER, pcount, &bwl);
*bw += bwl; *bw += bwl;
} }

View File

@ -3,17 +3,11 @@
#include "common.h" #include "common.h"
#include "ff.h" #include "ff.h"
typedef struct {
u8 iv[16];
u8 keyy[16];
u32 keyslot;
} __attribute__((packed)) XFIL;
// wrapper functions for ff.h // wrapper functions for ff.h
// incomplete(!) extension to FatFS to support on-the-fly crypto & path aliases // incomplete(!) extension to FatFS to support on-the-fly crypto & path aliases
FRESULT fx_open (FIL* fp, XFIL* xfp, const TCHAR* path, BYTE mode); FRESULT fx_open (FIL* fp, const TCHAR* path, BYTE mode);
FRESULT fx_read (FIL* fp, XFIL* xfp, void* buff, UINT btr, UINT* br); FRESULT fx_read (FIL* fp, void* buff, UINT btr, UINT* br);
FRESULT fx_write (FIL* fp, XFIL* xfp, const void* buff, UINT btw, UINT* bw); FRESULT fx_write (FIL* fp, const void* buff, UINT btw, UINT* bw);
void dealias_path (TCHAR* alias, const TCHAR* path); void dealias_path (TCHAR* alias, const TCHAR* path);
FRESULT fa_open (FIL* fp, const TCHAR* path, BYTE mode); FRESULT fa_open (FIL* fp, const TCHAR* path, BYTE mode);

View File

@ -343,10 +343,10 @@ bool FileSetData(const char* path, const u8* data, size_t size, size_t foffset,
if (drvtype & DRV_FAT) { if (drvtype & DRV_FAT) {
UINT bytes_written = 0; UINT bytes_written = 0;
FIL file; FIL file;
if (fa_open(&file, path, FA_WRITE | (create ? FA_CREATE_ALWAYS : FA_OPEN_ALWAYS)) != FR_OK) if (fx_open(&file, path, FA_WRITE | (create ? FA_CREATE_ALWAYS : FA_OPEN_ALWAYS)) != FR_OK)
return false; return false;
f_lseek(&file, foffset); f_lseek(&file, foffset);
f_write(&file, data, size, &bytes_written); fx_write(&file, data, size, &bytes_written);
f_close(&file); f_close(&file);
return (bytes_written == size); return (bytes_written == size);
} else if (drvtype & DRV_VIRTUAL) { } else if (drvtype & DRV_VIRTUAL) {
@ -363,10 +363,10 @@ size_t FileGetData(const char* path, u8* data, size_t size, size_t foffset) {
if (drvtype & DRV_FAT) { if (drvtype & DRV_FAT) {
UINT bytes_read = 0; UINT bytes_read = 0;
FIL file; FIL file;
if (fa_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return 0; return 0;
f_lseek(&file, foffset); f_lseek(&file, foffset);
if (f_read(&file, data, size, &bytes_read) != FR_OK) { if (fx_read(&file, data, size, &bytes_read) != FR_OK) {
f_close(&file); f_close(&file);
return 0; return 0;
} }
@ -423,7 +423,7 @@ bool FileGetSha256(const char* path, u8* sha256) {
FIL file; FIL file;
size_t fsize; size_t fsize;
if (fa_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return false; return false;
fsize = f_size(&file); fsize = f_size(&file);
f_lseek(&file, 0); f_lseek(&file, 0);
@ -431,7 +431,7 @@ bool FileGetSha256(const char* path, u8* sha256) {
for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) { for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT bytes_read = 0; UINT bytes_read = 0;
if (f_read(&file, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK) if (fx_read(&file, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK)
ret = false; ret = false;
if (!ShowProgress(pos + bytes_read, fsize, path)) if (!ShowProgress(pos + bytes_read, fsize, path))
ret = false; ret = false;
@ -502,7 +502,7 @@ bool FileInjectFile(const char* dest, const char* orig, u32 offset) {
dsize = dvfile.size; dsize = dvfile.size;
} else { } else {
vdest = false; vdest = false;
if (fa_open(&dfile, dest, FA_WRITE | FA_OPEN_EXISTING) != FR_OK) if (fx_open(&dfile, dest, FA_WRITE | FA_OPEN_EXISTING) != FR_OK)
return false; return false;
dsize = f_size(&dfile); dsize = f_size(&dfile);
f_lseek(&dfile, offset); f_lseek(&dfile, offset);
@ -519,7 +519,7 @@ bool FileInjectFile(const char* dest, const char* orig, u32 offset) {
osize = ovfile.size; osize = ovfile.size;
} else { } else {
vorig = false; vorig = false;
if (fa_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) { if (fx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) {
if (!vdest) f_close(&dfile); if (!vdest) f_close(&dfile);
return false; return false;
} }
@ -542,12 +542,12 @@ bool FileInjectFile(const char* dest, const char* orig, u32 offset) {
UINT read_bytes = min(MAIN_BUFFER_SIZE, osize - pos); UINT read_bytes = min(MAIN_BUFFER_SIZE, osize - pos);
UINT bytes_read = read_bytes; UINT bytes_read = read_bytes;
UINT bytes_written = read_bytes; UINT bytes_written = read_bytes;
if ((!vorig && (f_read(&ofile, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK)) || if ((!vorig && (fx_read(&ofile, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK)) ||
(vorig && ReadVirtualFile(&ovfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0)) (vorig && ReadVirtualFile(&ovfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0))
ret = false; ret = false;
if (!ShowProgress(pos + (bytes_read / 2), osize, orig)) if (!ShowProgress(pos + (bytes_read / 2), osize, orig))
ret = false; ret = false;
if ((!vdest && (f_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK)) || if ((!vdest && (fx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK)) ||
(vdest && WriteVirtualFile(&dvfile, MAIN_BUFFER, offset + pos, read_bytes, NULL) != 0)) (vdest && WriteVirtualFile(&dvfile, MAIN_BUFFER, offset + pos, read_bytes, NULL) != 0))
ret = false; ret = false;
if (bytes_read != bytes_written) if (bytes_read != bytes_written)
@ -616,7 +616,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) {
FIL ofile; FIL ofile;
u32 osize; u32 osize;
if (fa_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) if (fx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return false; return false;
f_lseek(&ofile, 0); f_lseek(&ofile, 0);
f_sync(&ofile); f_sync(&ofile);
@ -654,7 +654,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) {
if (!ShowProgress(0, 0, orig)) ret = false; if (!ShowProgress(0, 0, orig)) ret = false;
for (size_t pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) { for (size_t pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT bytes_read = 0; UINT bytes_read = 0;
if (f_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK) if (fx_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK)
ret = false; ret = false;
if (!ShowProgress(pos + (bytes_read / 2), osize, orig)) if (!ShowProgress(pos + (bytes_read / 2), osize, orig))
ret = false; ret = false;
@ -703,7 +703,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) {
} }
} }
if (fa_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) if (fx_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK)
return false; return false;
f_lseek(&dfile, 0); f_lseek(&dfile, 0);
f_sync(&dfile); f_sync(&dfile);
@ -722,7 +722,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) {
ret = false; ret = false;
if (!ShowProgress(pos + (read_bytes / 2), osize, orig)) if (!ShowProgress(pos + (read_bytes / 2), osize, orig))
ret = false; ret = false;
if (f_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) if (fx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK)
ret = false; ret = false;
if (read_bytes != bytes_written) if (read_bytes != bytes_written)
ret = false; ret = false;
@ -845,7 +845,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
FIL dfile; FIL dfile;
size_t fsize; size_t fsize;
if (fa_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) if (fx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return false; return false;
fsize = f_size(&ofile); fsize = f_size(&ofile);
if (GetFreeSpace(dest) < fsize) { if (GetFreeSpace(dest) < fsize) {
@ -854,7 +854,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
return false; return false;
} }
if (fa_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { if (fx_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) {
ShowPrompt(false, "Error: Cannot create destination file"); ShowPrompt(false, "Error: Cannot create destination file");
f_close(&ofile); f_close(&ofile);
return false; return false;
@ -869,11 +869,11 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) { for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT bytes_read = 0; UINT bytes_read = 0;
UINT bytes_written = 0; UINT bytes_written = 0;
if (f_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK) if (fx_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK)
ret = false; ret = false;
if (!ShowProgress(pos + (bytes_read / 2), fsize, orig)) if (!ShowProgress(pos + (bytes_read / 2), fsize, orig))
ret = false; ret = false;
if (f_write(&dfile, MAIN_BUFFER, bytes_read, &bytes_written) != FR_OK) if (fx_write(&dfile, MAIN_BUFFER, bytes_read, &bytes_written) != FR_OK)
ret = false; ret = false;
if (bytes_read != bytes_written) if (bytes_read != bytes_written)
ret = false; ret = false;