https://bitbucket.org/daniel_fort/magic-lantern
Raw File
Tip revision: b8e10f4e19dbb14404f332aeee9fa01d874ec686 authored by Daniel Fort on 01 May 2018, 19:48:39 UTC
Closed branch new-dryos-task-hooks_1200D.102
Tip revision: b8e10f4
io_decrypt.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.
 */
 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

#include "io_crypt.h"
#include "crypt_lfsr64.h"
#include "crypt_xtea.h"
#include "crypt_rsa.h"
#include "hash_password.h"


#define BLOCKSIZE (8 * 1024)


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";


static uint32_t lfsr113[] = { 0x00009821, 0x00098722, 0x00986332, 0x961FEFA7 };

void rand_fill(uint32_t *buffer, uint32_t length)
{
    for(uint32_t pos = 0; pos < length; pos++)
    {
        lfsr113[0] = ((lfsr113[0] & 0xFFFFFFFE) << 18) ^ (((lfsr113[0] <<  6) ^ lfsr113[0]) >> 13);
        lfsr113[1] = ((lfsr113[1] & 0xFFFFFFF8) <<  2) ^ (((lfsr113[1] <<  2) ^ lfsr113[1]) >> 27);
        lfsr113[2] = ((lfsr113[2] & 0xFFFFFFF0) <<  7) ^ (((lfsr113[2] << 13) ^ lfsr113[2]) >> 21);
        lfsr113[3] = ((lfsr113[3] & 0xFFFFFF80) << 13) ^ (((lfsr113[3] <<  3) ^ lfsr113[3]) >> 12);

        buffer[pos] = lfsr113[0] ^ lfsr113[1] ^ lfsr113[2] ^ lfsr113[3];
    }
}

void rand_seed(uint32_t seed)
{
    uint32_t tmp = 0;

    /* apply seed to internal states and do a few rounds to equally distribute seed bits */
    for(int loops = 0; loops < 128; loops++)
    {
        lfsr113[loops%4] ^= seed;
        rand_fill((uint32_t *)&tmp, 1);
    }
}

/* this routine checks if the LFSR64 encryption is handling all cases correctly */
static void io_decrypt_test()
{
    uint64_t key = 0xDEADBEEFDEADBEEF;
    uint32_t lfsr_blocksize = 0x00000224;
    crypt_cipher_t crypt_ctx;

    /* initialize encryption with some common parameters */
    crypt_lfsr64_init(&crypt_ctx, key);
    crypt_ctx.set_blocksize(&crypt_ctx, lfsr_blocksize);
    
    rand_seed(0x12341234);
    
    uint32_t bufsize = 1 * 1024 * 1024;
    uint8_t *buf_src = malloc(bufsize);
    uint8_t *buf_dst = malloc(bufsize);
    
    for(int loop = 0; loop < 1000; loop++)
    {
        /* prepare both buffers */
        rand_fill((uint32_t *)buf_src, bufsize / 4);
        memcpy(buf_dst, buf_src, bufsize);
        
        /* forge some test start and length */
        uint32_t start = 0;
        uint32_t length = 0;
        
        rand_fill((uint32_t *)&start, 1);
        rand_fill((uint32_t *)&length, 1);
        
        start %= bufsize;
        length %= (bufsize - start + 1);
        
        /* now do en- and de-cryption */
        printf("#%03d 0x%08X 0x%08X\n", loop, start, length);
        crypt_ctx.encrypt(&crypt_ctx, &buf_dst[start], &buf_src[start], length, 0);
        crypt_ctx.decrypt(&crypt_ctx, &buf_dst[start], &buf_dst[start], length, 0);
        
        /* check if both match */
        if(memcmp(buf_src, buf_dst, bufsize))
        {
            printf("  --> Check failed!!\n");
            return;
        }
    }
    
    free(buf_src);
    free(buf_dst);
}

static crypt_cipher_t iocrypt_rsa_ctx;
int main(int argc, char *argv[])
{
    //io_decrypt_test();
    //crypt_rsa_test();
    
    if(argc < 2)
    {
        printf("Usage: '%s <infile> [outfile] [password]\n", argv[0]);
        return -1;
    }
    
    uint64_t key = 0;
    uint32_t lfsr_blocksize = 0x00020000;
    crypt_cipher_t crypt_ctx;
    
    char *in_filename = argv[1];
    char *out_filename = malloc(strlen(in_filename) + 9);
    
    sprintf(out_filename, "%s_out.cr2", in_filename);
    
    if(argc >= 3)
    {
        out_filename = strdup(argv[2]);
    }
    
    /* password is optional */
    if(argc >= 4)
    {
        /* hash the password */
        hash_password(argv[3], &key);
    }
    
    /* open files */
    FILE *in_file = fopen(in_filename, "rb");
    if(!in_file)
    {
        printf("Could not open '%s'\n", in_filename);
        return -1;
    } 
    
    char *buffer = malloc(BLOCKSIZE);
    
    
    /* try to detect file type */
    if(fread(buffer, 1, 4, in_file) != 4)
    {
        printf("Could not read '%s'\n", in_filename);
        return -1;
    }
    
    if(!memcmp(buffer, jpg_magic, 4))
    {
        printf("File type: JPEG (plain)\n");
        return 0;
    }
    else if(!memcmp(buffer, cr2_magic, 4))
    {
        printf("File type: CR2 (plain)\n");
        return 0;
    }
    else if(!memcmp(buffer, lfsr_magic, 4))
    {
        printf("File type: LFSR64\n");
        
        if(!key)
        {
            printf("Error: Please specify a password\n");
            return -2;
        }
        if(fread(&lfsr_blocksize, 1, sizeof(uint32_t), in_file) != sizeof(uint32_t))
        {
            printf("Could not read '%s'\n", in_filename);
            return -1;
        }
        
        fseek(in_file, 0x200, SEEK_SET);
        crypt_lfsr64_init(&crypt_ctx, key);
        crypt_ctx.set_blocksize(crypt_ctx.priv, lfsr_blocksize);
    }
    else if(!memcmp(buffer, xtea_magic, 4))
    {
        printf("File type: XTEA\n");
        
        if(!key)
        {
            printf("Error: Please specify a password\n");
            return -2;
        }
        if(fread(&lfsr_blocksize, 1, sizeof(uint32_t), in_file) != sizeof(uint32_t))
        {
            printf("Could not read '%s'\n", in_filename);
            return -1;
        }
        
        fseek(in_file, 0x200, SEEK_SET);
        
        /* todo: fill it correctly */
        uint32_t password[4];
        memset(password, 0x00, sizeof(password));
        crypt_xtea_init(&crypt_ctx, password, key);
    }
    else if(!memcmp(buffer, rsa_magic, 4))
    {
        printf("File type: RSA+LFSR64\n");
        
        crypt_rsa_init(&iocrypt_rsa_ctx);
        
        if(crypt_rsa_get_keysize(iocrypt_rsa_ctx.priv) < 64)
        {
            printf("Invalid key size\n");
            return -1;
        }

        uint32_t encrypted_size = 0;
        
        if(fread(&encrypted_size, 1, sizeof(uint32_t), in_file) != sizeof(uint32_t))
        {
            printf("Could not read '%s'\n", in_filename);
            return -1;
        }
        
        if(fread(&lfsr_blocksize, 1, sizeof(uint32_t), in_file) != sizeof(uint32_t))
        {
            printf("Could not read '%s'\n", in_filename);
            return -1;
        }

        if(!encrypted_size || encrypted_size > 32768 * 4)
        {
            printf("encrypted_size: %d\n", encrypted_size);
            return -1;
        }
        
        if(!lfsr_blocksize || lfsr_blocksize > 0x10000000)
        {
            printf("lfsr_blocksize: %d\n", lfsr_blocksize);
            return -1;
        }
        
        printf("lfsr_blocksize: %d\n", lfsr_blocksize);
        printf("encrypted_size: %d\n", encrypted_size);
        
        char *encrypted = malloc(encrypted_size);
        if(fread(encrypted, 1, encrypted_size, in_file) != encrypted_size)
        {
            printf("Could not read '%s'\n", in_filename);
            return -1;
        }
        uint32_t decrypted_size = iocrypt_rsa_ctx.decrypt(iocrypt_rsa_ctx.priv, (uint8_t *)encrypted, (uint8_t *)encrypted, encrypted_size, 0);

        if(!decrypted_size || decrypted_size > encrypted_size)
        {
            printf("decrypted_size: %d. maybe key mismatch?\n", decrypted_size);
            return -1;
        }
        
        /* that decrypted data is the file crypt key */
        memcpy(&key, encrypted, sizeof(uint64_t));
        
        free(encrypted);
    
        uint32_t used_header = 4 + sizeof(uint32_t) + encrypted_size;
        uint32_t aligned_header = (used_header + 0x1FF) & ~0x1FF;
        
        /* now skip that header and continue with LFSR64 decryption */
        fseek(in_file, aligned_header, SEEK_SET);
        
        crypt_lfsr64_init(&crypt_ctx, key);
        crypt_ctx.set_blocksize(crypt_ctx.priv, lfsr_blocksize);
    }
    else if(!memcmp(buffer, rsaxtea_magic, 4))
    {
        printf("File type: RSA+XTEA\n");
        
        crypt_rsa_init(&iocrypt_rsa_ctx);
        
        if(crypt_rsa_get_keysize(iocrypt_rsa_ctx.priv) < 64)
        {
            printf("Invalid key size\n");
            return -1;
        }

        uint32_t encrypted_size = 0;
        
        if(fread(&encrypted_size, 1, sizeof(uint32_t), in_file) != sizeof(uint32_t))
        {
            printf("Could not read '%s'\n", in_filename);
            return -1;
        }
        
        if(fread(&lfsr_blocksize, 1, sizeof(uint32_t), in_file) != sizeof(uint32_t))
        {
            printf("Could not read '%s'\n", in_filename);
            return -1;
        }

        if(!encrypted_size || encrypted_size > 32768 * 4)
        {
            printf("encrypted_size: %d\n", encrypted_size);
            return -1;
        }
        
        if(!lfsr_blocksize || lfsr_blocksize > 0x10000000)
        {
            printf("lfsr_blocksize: %d\n", lfsr_blocksize);
            return -1;
        }
        
        printf("encrypted_size: %d\n", encrypted_size);
        
        char *encrypted = malloc(encrypted_size);
        if(fread(encrypted, 1, encrypted_size, in_file) != encrypted_size)
        {
            printf("Could not read '%s'\n", in_filename);
            return -1;
        }
        uint32_t decrypted_size = iocrypt_rsa_ctx.decrypt(iocrypt_rsa_ctx.priv, (uint8_t *)encrypted, (uint8_t *)encrypted, encrypted_size, 0);

        if(!decrypted_size || decrypted_size > encrypted_size)
        {
            printf("decrypted_size: %d. maybe key mismatch?\n", decrypted_size);
            return -1;
        }
        
        /* that decrypted data is the file crypt key */
        memcpy(&key, encrypted, sizeof(uint64_t));
        
        free(encrypted);
    
        uint32_t used_header = 4 + sizeof(uint32_t) + encrypted_size;
        uint32_t aligned_header = (used_header + 0x1FF) & ~0x1FF;
        
        /* now skip that header and continue with LFSR113 decryption */
        fseek(in_file, aligned_header, SEEK_SET);
        
        /* todo: fill it correctly */
        uint32_t password[4];
        memset(password, 0x00, sizeof(password));
        crypt_xtea_init(&crypt_ctx, password, key);
    }
    else
    {
        if(key)
        {
            printf("File type: unknown. assuming LFSR64\n");
            crypt_lfsr64_init(&crypt_ctx, key);
        }
        else
        {
            printf("File type: unknown. assuming LFSR64. Please specify a password\n");
            return -2;
        }
    }
    
    /* setup cipher with that hash */
    uint32_t first = 1;
    FILE *out_file = NULL;
    
    
    uint32_t file_offset = 0;
    
    
    while(!feof(in_file))
    {
        int ret = fread(buffer, 1, BLOCKSIZE, in_file);
        
        if(ret > 0)
        {
            crypt_ctx.decrypt(crypt_ctx.priv, (uint8_t *)buffer, (uint8_t *)buffer, ret, file_offset);
            
            /* try to detect file type */
            if(first)
            {
                first = 0;
                
                if(!memcmp(buffer, jpg_magic, 4))
                {
                    printf("File type: JPEG (decrypted)\n");
                }
                else if(!memcmp(buffer, cr2_magic, 4))
                {
                    printf("File type: CR2 (decrypted)\n");
                }
                else
                {
                    printf("File type: unknown. invalid key?\n");
                    fclose(in_file);
                    free(out_filename);
                    return 0;
                }
                
                out_file = fopen(out_filename, "w");
                if(!out_file)
                {
                    printf("Could not open '%s'\n", out_filename);
                    return -1;
                }
            }
            
            fwrite(buffer, 1, ret, out_file);
            file_offset += ret;
        }
        if(ret < 0)
        {
            printf("Could not read '%s'\n", in_filename);
            break;
        }
    }
    
    fclose(in_file);
    fclose(out_file);
    free(out_filename);
    return 0;
}
back to top