https://bitbucket.org/daniel_fort/magic-lantern
Tip revision: b8e10f4e19dbb14404f332aeee9fa01d874ec686 authored by Daniel Fort on 01 May 2018, 19:48:39 UTC
Closed branch new-dryos-task-hooks_1200D.102
Closed branch new-dryos-task-hooks_1200D.102
Tip revision: b8e10f4
io_crypt.c
/*
* Copyright (C) 2013 Magic Lantern Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/*
About how to find the model specific memory addresses
(example 7D v2.0.3, slave)
finding iodev_table
1. starting point FIO_ReadFile at 0xFF1FC434
2. if file handle id is < 100, it enters 0xFF3279F4
3. there the first call is 0xFF08CFA8
4. this function references to 0x2D3B0 (pointers to tables of functions) and calls a function from there using BX
5. as we (always) use the second entry in that table, our iodev_table = 0x2D3B8 (it points to 0xFF58B350)
the function tables have these functions:
0xFF58B350:
DCD iodev_OpenFile
DCD iodev_CloseFile
DCD iodev_unsupported
DCD iodev_ReadFile
DCD iodev_WriteFile
DCD iodev_unsupported
DCD iodev_unsupported
DCD iodev_unsupported_2
DCD iodev_unsupported
finding iodev_ctx (to be optimized away..)
1. go to iodev_OpenFile, which is at 0xFF458BBC (referenced in table found above)
2. enter the 3rd function 0xFF3E0D50. its the one with return value being checked (SUBS R5, R0, #0)
3. the first function (0xFF3E0958) being entered is allocating an fd.
4. this function references to 0x85510, so iodev_ctx = 0x85510
finding iodev_ctx_size (to be optimized away..)
1. the same function as you used above (0xFF3E0958) goes through all entries
2. it adds 0x18 bytes per iteration, so iodev_ctx_size = 0x18
*/
#include <module.h>
#include <dryos.h>
#include <property.h>
#include <bmp.h>
#include <menu.h>
#include <config.h>
#include <string.h>
#include <rand.h>
#include <io_crypt.h>
#include "crypt_lfsr64.h"
#include "crypt_xtea.h"
#include "crypt_rsa.h"
#include "hash_password.h"
#include "../trace/trace.h"
#include "../ime_base/ime_base.h"
uint32_t iocrypt_trace_ctx = TRACE_ERROR;
extern void gui_open_menu();
/* magics used to detect or set file type */
static const uint8_t cr2_magic[] = "\x49\x49\x2A\x00";
static const uint8_t jpg_magic[] = "\xff\xd8\xff\xe1";
static const uint8_t rsa_magic[] = "\xff\xd8\xff\xd8";
static const uint8_t lfsr_magic[] = "\xff\xd8\xff\x8d";
static const uint8_t xtea_magic[] = "\xff\xd8\xff\x8e";
static const uint8_t rsaxtea_magic[] = "\xff\xd8\xff\xd9";
/* FIO function hooks */
static iodev_handlers_t *orig_iodev = NULL;
static iodev_handlers_t hook_iodev;
/* those are model specific */
static uint32_t iodev_table = 0;
static uint32_t iodev_ctx = 0;
static uint32_t iodev_ctx_size = 0;
/* every file descriptor gets its own entry in this table */
static fd_map_t iocrypt_files[32];
/* scratch memory is used to put encrypted data into before it gets written */
static uint8_t *iocrypt_scratch = NULL;
/* current encryption key, derieved from password being entered */
static uint64_t iocrypt_key = 0;
/* status for RSA key generation */
static uint32_t iocrypt_rsa_key_active = 0;
/* RSA has a static context */
static crypt_cipher_t iocrypt_rsa_ctx;
/* variables needed to set up password */
static struct semaphore *iocrypt_password_sem = 0;
static char iocrypt_ime_text[128];
/* crypt thread data */
struct msg_queue *iocrypt_msgs = NULL;
static uint32_t iocrypt_shutdown = 0;
/* en-/decrypt on the fly with a symmetric cipher and given password */
#define CRYPT_MODE_SYMMETRIC 0
/* encrypt on the fly using a random key which gets stored using an asymmetric cipher. decryption on PC only. */
#define CRYPT_MODE_ASYMMETRIC 1
#define CRYPT_MODE_ASYMMETRIC_PARANOID 2
/* same ciphers, but files are stored unencrypted and get encrypted when camera is idle */
#define CRYPT_MODE_BACKGROUND_SYM 3
#define CRYPT_MODE_BACKGROUND_ASYM 4
/* dont redirect any file to fake images */
#define CRYPT_FAKE_OFF 0
/* if encrypytion key is unknown, show fake image */
#define CRYPT_FAKE_UNREADABLE 1
/* if the file is encrypted, no matter if key is correct or not, show fake image */
#define CRYPT_FAKE_ENCRYPTED 2
/* all images are replaced with fake ones */
#define CRYPT_FAKE_ALL 3
static CONFIG_INT("io_crypt.enabled", iocrypt_enabled, 0);
static CONFIG_INT("io_crypt.mode", iocrypt_mode, 0);
//static CONFIG_INT("io_crypt.fake", iocrypt_fake, 0);
static CONFIG_INT("io_crypt.block_size", iocrypt_block_size, 4);
static CONFIG_INT("io_crypt.ask_pass", iocrypt_ask_pass, 0);
static CONFIG_INT("io_crypt.rsa_key_size", iocrypt_rsa_key_size, 2);
static IME_UPDATE_FUNC(iocrypt_ime_update)
{
return IME_OK;
}
static IME_DONE_FUNC(iocrypt_ime_done)
{
//gui_stop_menu();
/* if key input dialog was cancelled */
if(status != IME_OK)
{
iocrypt_key = 0;
NotifyBox(2000, "Crypto disabled");
beep();
give_semaphore(iocrypt_password_sem);
return IME_OK;
}
hash_password((char *)text, &iocrypt_key);
/* done, use that key */
give_semaphore(iocrypt_password_sem);
return IME_OK;
}
/* ToDo: these directly read/write the current FIO position by altering FIO private data. is FIO_SeekFile safe to use here? */
static uint32_t iodev_GetPosition(uint32_t fd)
{
uint32_t *ctx = (uint32_t *)(iodev_ctx + iodev_ctx_size * fd);
return ctx[2];
}
static void iodev_SetPosition(uint32_t fd, uint32_t pos)
{
uint32_t *ctx = (uint32_t *)(iodev_ctx + iodev_ctx_size * fd);
ctx[2] = pos;
}
/* these are the iodev hooks */
static uint32_t hook_iodev_CloseFile(uint32_t fd)
{
trace_write(iocrypt_trace_ctx, "iodev_CloseFile(%d)", fd);
if(fd < COUNT(iocrypt_files))
{
if(iocrypt_files[fd].crypt_ctx.priv)
{
iocrypt_files[fd].crypt_ctx.deinit(iocrypt_files[fd].crypt_ctx.priv);
iocrypt_files[fd].crypt_ctx.priv = NULL;
}
}
uint32_t ret = orig_iodev->CloseFile(fd);
return ret;
}
static uint32_t iocrypt_asym_init(int fd)
{
uint64_t file_key = 0;
uint32_t password[4];
uint32_t lfsr_blocksize = (16 << iocrypt_block_size);
uint32_t blocksize = crypt_rsa_blocksize(iocrypt_rsa_ctx.priv);
trace_write(iocrypt_trace_ctx, "iocrypt_save_asym_hdr: block size %d bytes, lfsr_blocksize %d bytes", blocksize, lfsr_blocksize);
if(!blocksize)
{
return 0;
}
char *key = malloc(blocksize + 32);
/* create a random per-file crypt key */
rand_fill((uint32_t*)key, blocksize / 4);
memcpy(&file_key, key, sizeof(uint64_t));
/* todo: fill it correctly */
memset(password, 0x00, sizeof(password));
/* encrypt the randomly generated per-file header with RSA public key */
volatile iocrypt_job_t job;
job.type = CRYPT_JOB_ENCRYPT;
job.semaphore = iocrypt_files[fd].semaphore;
job.ctx = &iocrypt_rsa_ctx;
job.dst = key;
job.buf = key;
job.length = blocksize;
job.fd_pos = 0;
trace_write(iocrypt_trace_ctx, "iocrypt_asym_init: encrypt");
msg_queue_post(iocrypt_msgs, (uint32_t)&job);
/* wait until worker finished */
take_semaphore(job.semaphore, 0);
trace_write(iocrypt_trace_ctx, "iocrypt_asym_init: encrypt done (%d)", job.ret);
uint32_t encrypted_size = job.ret;
/* write header, block size and encrypted key */
orig_iodev->WriteFile(fd, (uint8_t*)rsaxtea_magic, 4);
orig_iodev->WriteFile(fd, (uint8_t*)&encrypted_size, sizeof(uint32_t));
orig_iodev->WriteFile(fd, (uint8_t*)&lfsr_blocksize, sizeof(uint32_t));
orig_iodev->WriteFile(fd, (uint8_t*)key, encrypted_size);
free(key);
uint32_t used_header = 4 + sizeof(uint32_t) + sizeof(uint32_t) + encrypted_size;
uint32_t aligned_header = (used_header + 0x1FF) & ~0x1FF;
uint32_t remain = aligned_header - used_header;
/* fill remaining space with random data */
char *filler = malloc(remain);
rand_fill((uint32_t*)filler, remain / 4);
orig_iodev->WriteFile(fd, (uint8_t*)filler, remain);
free(filler);
iocrypt_files[fd].header_size = aligned_header;
/* rewind file */
iodev_SetPosition(fd, 0);
/* init encryption */
iocrypt_files[fd].file_key = file_key;
//crypt_lfsr64_init(&iocrypt_files[fd].crypt_ctx, iocrypt_files[fd].file_key);
crypt_xtea_init(&iocrypt_files[fd].crypt_ctx, password, iocrypt_files[fd].file_key);
iocrypt_files[fd].crypt_ctx.set_blocksize(iocrypt_files[fd].crypt_ctx.priv, lfsr_blocksize);
return 1;
}
static uint32_t iocrypt_sym_init(int fd)
{
uint32_t password[4];
uint32_t lfsr_blocksize = (16 << iocrypt_block_size);
trace_write(iocrypt_trace_ctx, "iocrypt_sym_init: lfsr_blocksize %d bytes", lfsr_blocksize);
/* write header, block size and encrypted key */
orig_iodev->WriteFile(fd, (uint8_t*)xtea_magic, 4);
orig_iodev->WriteFile(fd, (uint8_t*)&lfsr_blocksize, sizeof(uint32_t));
iocrypt_files[fd].header_size = 0x200;
uint32_t remain = 0x200 - 8;
/* fill remaining space with random data */
char *filler = malloc(remain);
rand_fill((uint32_t*)filler, remain / 4);
orig_iodev->WriteFile(fd, (uint8_t*)filler, remain);
free(filler);
/* rewind file */
iodev_SetPosition(fd, 0);
/* todo: fill it correctly */
memset(password, 0x00, sizeof(password));
/* init encryption */
iocrypt_files[fd].file_key = iocrypt_key;
//crypt_lfsr64_init(&iocrypt_files[fd].crypt_ctx, iocrypt_files[fd].file_key);
crypt_xtea_init(&iocrypt_files[fd].crypt_ctx, password, iocrypt_files[fd].file_key);
iocrypt_files[fd].crypt_ctx.set_blocksize(iocrypt_files[fd].crypt_ctx.priv, lfsr_blocksize);
return 1;
}
static uint32_t hook_iodev_OpenFile(void *iodev, char *filename, int32_t flags, char *filename_)
{
int decryptable = 0;
uint32_t fd = orig_iodev->OpenFile(iodev, filename, flags, filename_);
trace_write(iocrypt_trace_ctx, "iodev_OpenFile('%s', %d) = %d", filename, flags, fd);
if(fd < COUNT(iocrypt_files) && iocrypt_enabled)
{
iocrypt_files[fd].crypt_ctx.priv = NULL;
iocrypt_files[fd].header_size = 0;
FIO_GetFileSize(filename, &iocrypt_files[fd].file_size);
/* copy filename */
strncpy(iocrypt_files[fd].filename, filename, sizeof(iocrypt_files[fd].filename));
char *ext = &filename[strlen(filename) - 3];
/* analyze file */
if(!strcmp(ext, "CR2") || !strcmp(ext, "JPG"))
{
/* when opening for read, first check if we really have to decrypt it */
if((flags & 3) == O_RDONLY)
{
uint8_t buf[4];
/* read and rewind file */
orig_iodev->ReadFile(fd, buf, 4);
iodev_SetPosition(fd, 0);
/* check for JPEG or CR2 header */
if(!memcmp(buf, jpg_magic, 4))
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be unencrypted JPEG", filename);
}
else if(!memcmp(buf, cr2_magic, 4))
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be unencrypted CR2", filename);
}
else if(!memcmp(buf, lfsr_magic, 4))
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be LFSR encrypted, try to decrypt on the fly", filename);
uint32_t lfsr_blocksize = 0;
iodev_SetPosition(fd, 4);
orig_iodev->ReadFile(fd, (uint8_t *)&lfsr_blocksize, 4);
iodev_SetPosition(fd, 0x200);
crypt_lfsr64_init(&iocrypt_files[fd].crypt_ctx, iocrypt_key);
iocrypt_files[fd].crypt_ctx.set_blocksize(iocrypt_files[fd].crypt_ctx.priv, lfsr_blocksize);
if(iocrypt_files[fd].crypt_ctx.priv)
{
/* read again to check if it is decryptable now */
orig_iodev->ReadFile(fd, buf, 4);
iodev_SetPosition(fd, 0);
iocrypt_files[fd].crypt_ctx.decrypt(iocrypt_files[fd].crypt_ctx.priv, buf, buf, 4, 0);
if(!memcmp(buf, jpg_magic, 4))
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be decryptable JPEG", filename);
decryptable = 1;
}
else if(!memcmp(buf, cr2_magic, 4))
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be decryptable CR2", filename);
decryptable = 1;
}
else
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be not decryptable", filename);
}
/* shall we crypt the file? if not, release context */
if(!decryptable)
{
iocrypt_files[fd].crypt_ctx.deinit(iocrypt_files[fd].crypt_ctx.priv);
iocrypt_files[fd].crypt_ctx.priv = NULL;
}
else
{
iocrypt_files[fd].header_size = 0x200;
}
}
}
else if(!memcmp(buf, xtea_magic, 4))
{
uint32_t password[4];
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be XTEA encrypted, try to decrypt on the fly", filename);
uint32_t lfsr_blocksize = 0;
iodev_SetPosition(fd, 4);
orig_iodev->ReadFile(fd, (uint8_t *)&lfsr_blocksize, 4);
iodev_SetPosition(fd, 0x200);
/* todo: fill it correctly */
memset(password, 0x00, sizeof(password));
crypt_xtea_init(&iocrypt_files[fd].crypt_ctx, password, iocrypt_key);
iocrypt_files[fd].crypt_ctx.set_blocksize(iocrypt_files[fd].crypt_ctx.priv, lfsr_blocksize);
if(iocrypt_files[fd].crypt_ctx.priv)
{
/* read again to check if it is decryptable now */
orig_iodev->ReadFile(fd, buf, 4);
iodev_SetPosition(fd, 0);
iocrypt_files[fd].crypt_ctx.decrypt(iocrypt_files[fd].crypt_ctx.priv, buf, buf, 4, 0);
if(!memcmp(buf, jpg_magic, 4))
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be decryptable JPEG", filename);
decryptable = 1;
}
else if(!memcmp(buf, cr2_magic, 4))
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be decryptable CR2", filename);
decryptable = 1;
}
else
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be not decryptable", filename);
}
/* shall we crypt the file? if not, release context */
if(!decryptable)
{
iocrypt_files[fd].crypt_ctx.deinit(iocrypt_files[fd].crypt_ctx.priv);
iocrypt_files[fd].crypt_ctx.priv = NULL;
}
else
{
iocrypt_files[fd].header_size = 0x200;
}
}
}
else if(!memcmp(buf, rsa_magic, 4))
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' seems to be RSA encrypted", filename);
}
else
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' is unknown", filename);
}
}
else
{
/* shall we crypt the file? */
switch(iocrypt_mode)
{
case CRYPT_MODE_SYMMETRIC:
if(iocrypt_key)
{
trace_write(iocrypt_trace_ctx, " ->> File '%s' will get encrypted with symmetric cipher", filename);
if(!iocrypt_sym_init(fd))
{
trace_write(iocrypt_trace_ctx, " FAILED TO SET UP ENCRYPTION");
beep();
NotifyBox(2000, "Some error happened, not encrypting!");
break;
}
}
else
{
beep();
NotifyBox(2000, "No key entered, not encrypting!");
}
break;
case CRYPT_MODE_ASYMMETRIC:
case CRYPT_MODE_ASYMMETRIC_PARANOID:
trace_write(iocrypt_trace_ctx, " ->> File '%s' will get encrypted with asymmetric cipher", filename);
if(!iocrypt_asym_init(fd))
{
trace_write(iocrypt_trace_ctx, " FAILED TO SET UP ENCRYPTION");
beep();
NotifyBox(2000, "RSA setup failed!");
break;
}
break;
default:
break;
}
}
}
/* log the encryption status */
if(iocrypt_files[fd].crypt_ctx.priv)
{
trace_write(iocrypt_trace_ctx, " ->> ENCRYPTED '%s'", filename);
}
else
{
trace_write(iocrypt_trace_ctx, " ->> plain '%s'", filename);
}
}
return fd;
}
static uint32_t hook_iodev_ReadFile(uint32_t fd, uint8_t *buf, uint32_t length)
{
if(!iocrypt_enabled)
{
return orig_iodev->ReadFile(fd, buf, length);
}
uint32_t fd_pos = iodev_GetPosition(fd);
/* when there is some encryption active, handle file offset */
if(iocrypt_files[fd].crypt_ctx.priv)
{
iodev_SetPosition(fd, iodev_GetPosition(fd) + iocrypt_files[fd].header_size);
}
uint32_t ret = orig_iodev->ReadFile(fd, buf, length);
/* when there is some encryption active, handle file offset */
if(iocrypt_files[fd].crypt_ctx.priv)
{
/* if reading was beyond file end, provide zeroes */
if(ret < length)
{
memset(&buf[ret], 0x00, length - ret);
ret = MIN(length, iocrypt_files[fd].file_size - fd_pos);
iodev_SetPosition(fd, fd_pos + ret);
}
iodev_SetPosition(fd, iodev_GetPosition(fd) - iocrypt_files[fd].header_size);
}
if(fd < COUNT(iocrypt_files))
{
trace_write(iocrypt_trace_ctx, "iodev_ReadFile(0x%08X, 0x%08X) -> %s, fd = %d, pos_before = 0x%08X, ret %d, pos_after %d", buf, length, iocrypt_files[fd].filename, fd, fd_pos, iocrypt_files[fd].filename, ret, iodev_GetPosition(fd));
if(iocrypt_files[fd].crypt_ctx.priv)
{
/* let the data being encrypted asynchronously */
volatile iocrypt_job_t job;
job.type = CRYPT_JOB_DECRYPT;
job.fd = fd;
job.semaphore = iocrypt_files[fd].semaphore;
job.ctx = &iocrypt_files[fd].crypt_ctx;
job.dst = buf;
job.buf = buf;
job.length = length;
job.fd_pos = fd_pos;
trace_write(iocrypt_trace_ctx, "iodev_ReadFile: decrypt");
msg_queue_post(iocrypt_msgs, (uint32_t)&job);
/* wait until worker finished */
take_semaphore(job.semaphore, 0);
trace_write(iocrypt_trace_ctx, "iodev_ReadFile: decrypt done");
}
}
return ret;
}
static uint32_t hook_iodev_WriteFile(uint32_t fd, uint8_t *buf, uint32_t length)
{
uint32_t ret = 0;
if(fd < COUNT(iocrypt_files) && iocrypt_enabled)
{
uint32_t misalign = ((uint32_t)buf) % 8;
uint32_t fd_pos = iodev_GetPosition(fd);
if(iocrypt_files[fd].crypt_ctx.priv)
{
trace_write(iocrypt_trace_ctx, "iodev_WriteFile pre(0x%08X, 0x%08X) -> %s, fd = %d, fd_pos = 0x%08X, misalign = %d", buf, length, iocrypt_files[fd].filename, fd, fd_pos, misalign);
/* when there is some encryption active, handle file offset */
iodev_SetPosition(fd, iodev_GetPosition(fd) + iocrypt_files[fd].header_size);
/* let the data being encrypted asynchronously */
volatile iocrypt_job_t job;
job.type = CRYPT_JOB_ENCRYPT_WRITE;
job.fd = fd;
job.semaphore = iocrypt_files[fd].semaphore;
job.ctx = &iocrypt_files[fd].crypt_ctx;
job.buf = buf;
job.length = length;
job.fd_pos = fd_pos;
trace_write(iocrypt_trace_ctx, "iodev_WriteFile: encrypt");
msg_queue_post(iocrypt_msgs, (uint32_t)&job);
/* wait until worker finished */
take_semaphore(job.semaphore, 0);
trace_write(iocrypt_trace_ctx, "iodev_WriteFile: encrypt done");
/* when there is some encryption active, handle file offset */
iodev_SetPosition(fd, iodev_GetPosition(fd) - iocrypt_files[fd].header_size);
trace_write(iocrypt_trace_ctx, "iodev_WriteFile post(0x%08X, 0x%08X) -> fd = %d, fd_pos = 0x%08X, fd_pos (now) = 0x%08X", buf, length, fd, fd_pos, iodev_GetPosition(fd));
return job.ret;
}
}
/* handle by default method */
ret = orig_iodev->WriteFile(fd, buf, length);
return ret;
}
static void iocrypt_enter_pw()
{
/* ensure there is only one dialog */
take_semaphore(iocrypt_password_sem, 0);
gui_open_menu();
if(!ime_base_start((unsigned char *)"io_crypt: Enter password", (unsigned char *)iocrypt_ime_text, sizeof(iocrypt_ime_text) - 1, IME_UTF8, IME_CHARSET_ANY, &iocrypt_ime_update, &iocrypt_ime_done, 0, 0, 0, 0))
{
give_semaphore(iocrypt_password_sem);
iocrypt_key = 0;
NotifyBox(2000, "IME error, Crypto disabled");
}
}
static void iocrypt_speed_test_write(char *file, uint32_t blocksize, uint32_t loops)
{
char filename[32];
uint8_t *buffer = malloc(blocksize);
if(!buffer)
{
return;
}
memset(buffer, 0x5A, blocksize);
snprintf(filename, sizeof(filename), "%s/%s", get_dcim_dir(), file);
FILE* f = FIO_CreateFile(filename);
if(!f)
{
free(buffer);
return;
}
for(uint32_t loop = 0; loop < loops; loop++)
{
FIO_WriteFile(f, buffer, blocksize);
}
FIO_CloseFile(f);
free(buffer);
}
static void iocrypt_speed_test_read(char *file, uint32_t blocksize)
{
char filename[32];
uint8_t *buffer = malloc(blocksize);
if(!buffer)
{
return;
}
snprintf(filename, sizeof(filename), "%s/%s", get_dcim_dir(), file);
FILE* f = FIO_OpenFile(filename, O_RDONLY | O_SYNC);
if(!f)
{
free(buffer);
return;
}
while(FIO_ReadFile(f, buffer, blocksize) == (int)blocksize)
{
}
FIO_CloseFile(f);
free(buffer);
}
static void iocrypt_speed_test()
{
uint32_t unset = 0;
uint32_t loops = 10;
uint32_t blocks = 20;
uint32_t blocksize = 1*1024*1024;
gui_stop_menu();
msleep(500);
hash_password("Speed test password", &iocrypt_key);
bmp_printf(FONT_MED, 10, 30, "Starting benchmark");
for(uint32_t loop = 0; loop < loops; loop++)
{
uint32_t start = 0;
uint32_t delta = 0;
uint32_t speed = 0;
start = get_ms_clock_value();
iocrypt_speed_test_write("IO_CRYPT.CR2", blocksize, blocks);
delta = get_ms_clock_value() - start;
speed = (blocksize / 1024) * blocks * 1000 * 10 / delta;
trace_write(iocrypt_trace_ctx, "iocrypt_speed_test: [crypted] write %d ms, %d.%02d MB/s", delta, speed/10, speed % 10);
bmp_printf(FONT_MED, 10, 60 + (4 * loop + 0) * font_med.height, "[crypted] write %d.%02d MB/s ", speed/10, speed % 10);
start = get_ms_clock_value();
iocrypt_speed_test_read("IO_CRYPT.CR2", blocksize);
delta = get_ms_clock_value() - start;
speed = (blocksize / 1024) * blocks * 1000 * 10 / delta;
trace_write(iocrypt_trace_ctx, "iocrypt_speed_test: [crypted] read %d ms, %d.%02d MB/s", delta, speed/10, speed % 10);
bmp_printf(FONT_MED, 10, 60 + (4 * loop + 1) * font_med.height, "[crypted] read %d.%02d MB/s ", speed/10, speed % 10);
start = get_ms_clock_value();
iocrypt_speed_test_write("IO_CRYPT.DAT", blocksize, blocks);
delta = get_ms_clock_value() - start;
speed = (blocksize / 1024) * blocks * 1000 * 10 / delta;
trace_write(iocrypt_trace_ctx, "iocrypt_speed_test: [plain] write %d ms, %d.%02d MB/s", delta, speed/10, speed % 10);
bmp_printf(FONT_MED, 10, 60 + (4 * loop + 2) * font_med.height, "[plain] write %d.%02d MB/s ", speed/10, speed % 10);
start = get_ms_clock_value();
iocrypt_speed_test_read("IO_CRYPT.DAT", blocksize);
delta = get_ms_clock_value() - start;
speed = (blocksize / 1024) * blocks * 1000 * 10 / delta;
trace_write(iocrypt_trace_ctx, "iocrypt_speed_test: [plain] read %d ms, %d.%02d MB/s", delta, speed/10, speed % 10);
bmp_printf(FONT_MED, 10, 60 + (4 * loop + 3) * font_med.height, "[plain] read %d.%02d MB/s ", speed/10, speed % 10);
FIO_RemoveFile("IO_CRYPT.CR2");
FIO_RemoveFile("IO_CRYPT.DAT");
}
if(unset)
{
iocrypt_key = 0;
}
bmp_printf(FONT_LARGE, 0, 60, "Test done");
beep();
}
void iocrypt_rsa_key_gen()
{
NotifyBox(600000, "Generating RSA key.\nThis takes a while!");
iocrypt_rsa_key_active = 1;
crypt_rsa_generate_keys(iocrypt_rsa_ctx.priv);
iocrypt_rsa_key_active = 0;
NotifyBoxHide();
beep();
NotifyBox(2000, "RSA key generated!");
}
static MENU_SELECT_FUNC(iocrypt_enter_pw_select)
{
iocrypt_enter_pw();
}
static MENU_SELECT_FUNC(iocrypt_rsa_key_select)
{
crypt_rsa_set_keysize(512 << iocrypt_rsa_key_size);
task_create("crypt_rsa_generate_keys", 0x1e, 0x1000, iocrypt_rsa_key_gen, NULL);
}
static MENU_SELECT_FUNC(iocrypt_test_rsa)
{
task_create("rsa_test", 0x1e, 0x1000, crypt_rsa_test, (void*)0);
}
static MENU_SELECT_FUNC(iocrypt_test_speed)
{
task_create("speed_test", 0x1e, 0x1000, iocrypt_speed_test, (void*)0);
}
static MENU_UPDATE_FUNC(iocrypt_update)
{
if(!iocrypt_enabled)
{
MENU_SET_VALUE("OFF");
return;
}
switch(iocrypt_mode)
{
case CRYPT_MODE_SYMMETRIC:
if(!iocrypt_key)
{
MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Please set a password first. Files are not encrypted yet.");
}
MENU_SET_VALUE("ON, Password");
break;
case CRYPT_MODE_ASYMMETRIC:
case CRYPT_MODE_ASYMMETRIC_PARANOID:
if(!crypt_rsa_get_pub(iocrypt_rsa_ctx.priv))
{
MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Please create keys first. Files are not encrypted yet.");
}
else if(crypt_rsa_get_priv(iocrypt_rsa_ctx.priv))
{
MENU_SET_WARNING(MENU_WARN_ADVICE, "Move IO_CRYPT.KEY to a safe place and DELETE from card.");
}
MENU_SET_VALUE("ON, RSA");
break;
}
if(iocrypt_rsa_key_active)
{
MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Still generating the RSA keys.");
}
}
static MENU_UPDATE_FUNC(iocrypt_rsa_key_update)
{
if(iocrypt_rsa_key_active)
{
MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Still generating the RSA keys.");
MENU_SET_VALUE("Generating, %d %%", crypt_rsa_get_keyprogress(iocrypt_rsa_ctx.priv));
}
}
static struct menu_entry iocrypt_menus[] =
{
{
.name = "Encryption",
.update = &iocrypt_update,
.priv = &iocrypt_enabled,
.max = 1,
.submenu_width = 710,
.children = (struct menu_entry[]) {
{
.name = "Encryption mode",
.priv = &iocrypt_mode,
.max = 1, /* the others are not implemented yet */
.icon_type = IT_DICE,
.choices = (const char *[]) {"Password", "RSA", "RSA (paranoid)", "Background PW", "Background RSA"},
.help = "Select the encryption mode. The higher the level, the less comfort you have.",
},
/*
{
.name = "Show fake images",
.priv = &iocrypt_fake,
.max = 3,
.choices = (const char *[]) {"OFF", "Wrong Password", "All Encrypted", "All Images"},
.help = "Which images should get replaced by fake images.",
},
*/
{
.name = "Set password",
.select = &iocrypt_enter_pw_select,
.priv = NULL,
.icon_type = IT_ACTION,
},
{
.name = "Blocksize",
.priv = &iocrypt_block_size,
.max = 9,
.icon_type = IT_DICE,
.choices = (const char *[]) {"16", "32", "64", "128", "256", "512", "1k", "2k", "4k", "8k"},
.help = "Blocks get encrypted with the same 64 bit key. The smaller the more secure but slower.",
},
{
.name = "Ask for password on startup",
.priv = &iocrypt_ask_pass,
.max = 1,
.help = "When enabled it will ask for the encryption password right after camera powerup.",
},
{
.name = "Create RSA Key",
.select = &iocrypt_rsa_key_select,
.update = &iocrypt_rsa_key_update,
.priv = NULL,
.icon_type = IT_ACTION,
.help = "Do this ONCE at HOME and then store /priv.key on your PC safely.",
},
{
.name = "RSA Keysize",
.priv = &iocrypt_rsa_key_size,
.max = 3,
.icon_type = IT_DICE,
.choices = (const char *[]) {"512", "1024", "2048", "4096"},
.help = "Key size when creating a RSA key pair. The smaller, the less security you have.",
},
{
.name = "Test: Speed",
.select = &iocrypt_test_speed,
.priv = NULL,
.icon_type = IT_ACTION,
},
{
.name = "Test: RSA",
.select = &iocrypt_test_rsa,
.priv = NULL,
.icon_type = IT_ACTION,
},
MENU_EOL,
},
},
};
static void iocrypt_task()
{
while(!iocrypt_shutdown && !ml_shutdown_requested)
{
int timeout = 500;
iocrypt_job_t *job = NULL;
/* fetch a new encryption job */
if(msg_queue_receive(iocrypt_msgs, &job, timeout))
{
continue;
}
switch(job->type)
{
/* combined encrypt and write job which is atomic */
case CRYPT_JOB_ENCRYPT_WRITE:
{
trace_write(iocrypt_trace_ctx, " ->> ENCRYPT");
job->ctx->encrypt(job->ctx->priv, iocrypt_scratch, job->buf, job->length, job->fd_pos);
trace_write(iocrypt_trace_ctx, " ->> WRITE");
job->ret = orig_iodev->WriteFile(job->fd, iocrypt_scratch, job->length);
trace_write(iocrypt_trace_ctx, " ->> DONE");
give_semaphore(job->semaphore);
break;
}
/* simple decryption */
case CRYPT_JOB_ENCRYPT:
{
trace_write(iocrypt_trace_ctx, " ->> ENCRYPT");
job->ret = job->ctx->encrypt(job->ctx->priv, job->dst, job->buf, job->length, job->fd_pos);
trace_write(iocrypt_trace_ctx, " ->> DONE (%d)", job->ret);
give_semaphore(job->semaphore);
break;
}
/* simple decryption */
case CRYPT_JOB_DECRYPT:
{
trace_write(iocrypt_trace_ctx, " ->> DECRYPT");
job->ret = job->ctx->decrypt(job->ctx->priv, job->dst, job->buf, job->length, job->fd_pos);
trace_write(iocrypt_trace_ctx, " ->> DONE");
give_semaphore(job->semaphore);
break;
}
}
}
iocrypt_shutdown = 0;
}
static unsigned int iocrypt_init()
{
/* for debugging */
if(0)
{
char filename[32] = "IO_CRYPT.TXT";
iocrypt_trace_ctx = trace_start("debug", filename);
trace_set_flushrate(iocrypt_trace_ctx, 1000);
trace_format(iocrypt_trace_ctx, TRACE_FMT_TIME_REL | TRACE_FMT_COMMENT, ' ');
trace_write(iocrypt_trace_ctx, "io_crypt: Starting trace");
}
/* clear file map */
for(int pos = 0; pos < COUNT(iocrypt_files); pos++)
{
iocrypt_files[pos].crypt_ctx.priv = NULL;
iocrypt_files[pos].semaphore = create_named_semaphore("iocrypt_pw", 0);
}
if(is_camera("600D", "1.0.2"))
{
trace_write(iocrypt_trace_ctx, "io_crypt: Detected 600D");
iodev_table = 0x1E684;
iodev_ctx = 0x7EB08;
iodev_ctx_size = 0x18;
}
else if(is_camera("7D", "2.0.3"))
{
trace_write(iocrypt_trace_ctx, "io_crypt: Detected 7D");
iodev_table = 0x2D3B8;
iodev_ctx = 0x85510;
iodev_ctx_size = 0x18;
}
else if(is_camera("60D", "1.1.1"))
{
trace_write(iocrypt_trace_ctx, "io_crypt: Detected 60D");
iodev_table = 0x3E2BC;
iodev_ctx = 0x5CB38;
iodev_ctx_size = 0x18;
}
else if(is_camera("5D3", "1.1.3"))
{
trace_write(iocrypt_trace_ctx, "io_crypt: Detected 5D3 1.1.3");
iodev_table = 0x44FA8;
iodev_ctx = 0x67140;
iodev_ctx_size = 0x20;
}
else if(is_camera("5D3", "1.2.3"))
{
trace_write(iocrypt_trace_ctx, "io_crypt: Detected 5D3 1.2.3");
iodev_table = 0x44F38;
iodev_ctx = 0x671D8;
iodev_ctx_size = 0x20;
}
else if(is_camera("EOSM", "2.0.2"))
{
iodev_table = 0x6F518;
iodev_ctx = 0x9C4A8;
iodev_ctx_size = 0x20;
}
else if(is_camera("6D", "1.1.6"))
{
iodev_table = 0x9DF18;
iodev_ctx = 0xCC130;
iodev_ctx_size = 0x20;
}
/*
else if(is_camera("650D", "1.0.4"))
{
trace_write(iocrypt_trace_ctx, "io_crypt: Detected 650D");
iodev_table = 0x54060;
iodev_ctx = 0x7C278;
iodev_ctx_size = 0x20;
}
else if(is_camera("50D", "1.0.9"))
{
trace_write(iocrypt_trace_ctx, "io_crypt: Detected 50D");
iodev_table = 0x1F208;
iodev_ctx = 0x49A64;
iodev_ctx_size = 0x18;
}
*/
else
{
NotifyBox(2000, "io_crypt: Camera unsupported");
return CBR_RET_ERROR;
}
iocrypt_password_sem = create_named_semaphore("iocrypt_pw", 1);
/* ask for the initial password */
if(iocrypt_ask_pass && iocrypt_enabled && (iocrypt_mode == CRYPT_MODE_SYMMETRIC || iocrypt_mode == CRYPT_MODE_BACKGROUND_SYM))
{
trace_write(iocrypt_trace_ctx, "io_crypt: Asking for password");
iocrypt_enter_pw();
}
/* this memory is used for buffering encryption, so we dont have to undo the changes in memory */
iocrypt_scratch = malloc(CRYPT_SCRATCH_SIZE);
/* now patch the iodev handlers */
orig_iodev = (iodev_handlers_t *)MEM(iodev_table);
hook_iodev = *orig_iodev;
hook_iodev.OpenFile = &hook_iodev_OpenFile;
hook_iodev.ReadFile = &hook_iodev_ReadFile;
hook_iodev.WriteFile = &hook_iodev_WriteFile;
hook_iodev.CloseFile = &hook_iodev_CloseFile;
MEM(iodev_table) = (uint32_t)&hook_iodev;
/* create a message queue for processing crypt tasks asyncrhonously */
iocrypt_msgs = (struct msg_queue *) msg_queue_create("iocrypt_msgs", 100);
crypt_rsa_init(&iocrypt_rsa_ctx);
task_create("iocrypt_task", 0x1A, 0x1000, iocrypt_task, (void*)0);
/* any file operation is routed through us now */
menu_add("Shoot", iocrypt_menus, COUNT(iocrypt_menus) );
return 0;
}
static unsigned int iocrypt_deinit()
{
iocrypt_shutdown = 1;
while(iocrypt_shutdown && !ml_shutdown_requested)
{
msleep(20);
}
MEM(iodev_table) = (uint32_t)orig_iodev;
if(iocrypt_scratch)
{
free(iocrypt_scratch);
}
return 0;
}
MODULE_INFO_START()
MODULE_INIT(iocrypt_init)
MODULE_DEINIT(iocrypt_deinit)
MODULE_INFO_END()
MODULE_CONFIGS_START()
MODULE_CONFIG(iocrypt_enabled)
MODULE_CONFIG(iocrypt_mode)
MODULE_CONFIG(iocrypt_block_size)
MODULE_CONFIG(iocrypt_ask_pass)
MODULE_CONFIG(iocrypt_rsa_key_size)
MODULE_CONFIGS_END()