crypt_lfsr64.c 11 KB
Newer Older
1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
 * 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.
 */
21

22
#ifdef MODULE
23

24
25
26
27
#include <dryos.h>
#include <property.h>
#include <bmp.h>
#include <menu.h>
28

29
#include "../trace/trace.h"
30

31
#else
32
33
34
35
36

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

g3gg0's avatar
g3gg0 committed
37
38
#define msleep(x) usleep((x)*1000)

39
#if defined(TRACE_DISABLED)
40
#define trace_write(x,...) do { (void)0; } while (0)
41
42
43
#else
#define trace_write(x,...) do { printf(__VA_ARGS__); printf("\n"); } while (0)
#endif
44
45

#endif
46

47
48
49
/* common includes */
#include <string.h>

50
51
#include "io_crypt.h"
#include "crypt_lfsr64.h"
52

53
54
55
56
57
58
59
/*  when write speed is about 70MiB/s unencrypted:
        blocksize 0x00002000: 2MiB/s
        blocksize 0x00010000: 5MiB/s
        blocksize 0x00020000: 20MiB/s
    the larger the block size is, the easier it is to reconstruct the current block key
        */
static uint32_t crypt_lfsr64_blocksize = 0x00020000;
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
extern uint32_t iocrypt_trace_ctx;

/* clock the current LFSR by given number of clocks */
static void crypt_lfsr64_clock(lfsr64_ctx_t *ctx, uint32_t clocks)
{
    uint64_t lfsr = ctx->lfsr_state;
    
    for(uint32_t clock = 0; clock < clocks; clock++)
    {
        /* maximum length LFSR according to http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf */
        int bit = ((lfsr >> 63) ^ (lfsr >> 62) ^ (lfsr >> 60) ^ (lfsr >> 59)) & 1;
        lfsr = (lfsr << 1) | bit;
    }
    
    ctx->lfsr_state = lfsr;
}

77
/* xor the buffer with 8/64 bit alignment */
78
79
80
81
82
static void crypt_lfsr64_xor_uint8(void *dst_in, void *src_in, lfsr64_ctx_t *ctx, uint32_t offset)
{
    uint8_t *dst = (uint8_t *)dst_in;
    uint8_t *src = (uint8_t *)src_in;
    
83
    *dst = *src ^ ctx->key_uint8[offset % 8];
84
85
86
87
88
89
90
}

static void crypt_lfsr64_xor_uint64(void *dst_in, void *src_in, lfsr64_ctx_t *ctx, uint32_t offset)
{
    uint64_t *dst = (uint64_t *)dst_in;
    uint64_t *src = (uint64_t *)src_in;
    
91
    *dst = *src ^ ctx->key_uint64[offset % 8];
92
93
94
95
96
}

static void update_key(lfsr64_ctx_t *ctx, uint32_t offset, uint32_t force)
{
    /* update the current encryption key whever reaching the next block */
97
    uint32_t block = offset / ctx->blocksize;
98
    
99
100
    //trace_write(iocrypt_trace_ctx, "update_key: offset 0x%08X, block 0x%04X, current_block 0x%04X", offset, block, ctx->current_block);
    
101
    if(!force)
102
    {
103
104
        if(ctx->current_block == block)
        {
105
            //trace_write(iocrypt_trace_ctx, "update_key: return, no update needed");
106
107
108
            return;
        }
    }
109
110
111
112
    else
    {
        //trace_write(iocrypt_trace_ctx, "update_key: forced update");
    }
113
114
115
    
    ctx->current_block = block;
    
116
    /* first initialize LFSR with base key and block */
117
    ctx->lfsr_state = ctx->password ^ block;
118
    crypt_lfsr64_clock(ctx, 111);
119
120
121
    
    /* then update it with the file offset again and shift by an amount based on file offset */
    ctx->lfsr_state ^= block;
122
    crypt_lfsr64_clock(ctx, (11 * block) % 16);
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
    
    /* mask it again */
    ctx->lfsr_state ^= ctx->password;
    
    trace_write(iocrypt_trace_ctx, "update_key: offset 0x%08X, password: 0x%08X%08X, key: 0x%08X%08X", offset, (uint32_t)(ctx->password>>32), (uint32_t)ctx->password, (uint32_t)(ctx->lfsr_state>>32), (uint32_t)ctx->lfsr_state);
    
    /* build an array with key elements for every offset in uint8_t mode */
    uint64_t lfsr = ctx->lfsr_state;
    for(int pos = 0; pos < 8; pos++)
    {
        ctx->key_uint8[pos] = (uint8_t)(lfsr & 0xFF);
        lfsr >>= 8;
    }
    
    /* build an array with key elements for every offset in uint64_t mode */
    for(int pos = 0; pos < 8; pos++)
    {
140
        uint32_t elem_addr = (uint32_t)&ctx->key_uint64[pos];
141
        
142
        memcpy((void*)elem_addr, &ctx->key_uint8[pos], 8 - pos);
g3gg0's avatar
g3gg0 committed
143
        memcpy((void*)(elem_addr + (8 - pos)), &ctx->key_uint8[0], pos);
144
145
146
    }
}

147
/* de-/encryption routine, XORing every 64 bit word with an offset based crypt key */
148
static uint32_t crypt_lfsr64_encrypt(crypt_priv_t *priv, uint8_t *dst_in, uint8_t *src_in, uint32_t in_length, uint32_t offset)
149
{
g3gg0's avatar
g3gg0 committed
150
    lfsr64_ctx_t *ctx = (lfsr64_ctx_t *)priv;
151
152
    uint8_t *dst = dst_in;
    uint8_t *src = src_in;
153
    uint32_t length = in_length;
154
155
156
    uint32_t blocksize = ctx->blocksize;
    
    //trace_write(iocrypt_trace_ctx, "crypt_lfsr64_encrypt: dst 0x%08X, src 0x%08X, length: 0x%08X, offset: 0x%08X, cipher_ctx: 0x%08X, priv: 0x%08X, blocksize: 0x%08X", dst_in, src_in, in_length, offset, cipher_ctx, cipher_ctx->priv, blocksize);
157
    
158
    /* ensure initial key creation if necessary */
159
    update_key(ctx, offset, 0); 
160
    
161
162
    //trace_write(iocrypt_trace_ctx, "crypt_lfsr64_encrypt: dst 0x%08X, src 0x%08X, length: 0x%08X, offset: 0x%08X", dst, src, length, offset);
    
163
    /* try to get the addresses aligned */
164
    while((((uint32_t)dst) % 8) && (length > 0))
165
    {
166
        /* at every block start, rerun key update */
167
        if((offset % blocksize) == 0)
168
169
170
171
        {
            update_key(ctx, offset, 0);
        }
        
172
        /* encrypt a single byte, thats very slow of course */
173
174
175
176
177
178
179
        crypt_lfsr64_xor_uint8(dst, src, ctx, offset);
        dst += 1;
        src += 1;
        offset += 1;
        length -= 1;
    }
    
180
    if(!length)
181
    {
182
        //trace_write(iocrypt_trace_ctx, "crypt_lfsr64_encrypt: all done dst 0x%08X, src 0x%08X, length: 0x%08X, offset: 0x%08X", dst, src, length, offset);
183
184
185
        return in_length;
    }
    
186
    /* the pointers are 64 bit aligned now. do 64 bit writes if possible. */
187
188
    if((offset % 8) == 0)
    {
189
        //trace_write(iocrypt_trace_ctx, "crypt_lfsr64_encrypt: (offset mod 8) == 0) dst 0x%08X, src 0x%08X, length: 0x%08X, offset: 0x%08X", dst, src, length, offset);
190
191
        /* processing loop for 64 bit writes at even file offsets. due to compiler optimizations this loop is faster */
        while(length >= 8)
192
        {
193
            if((offset % blocksize) == 0)
194
195
196
197
198
199
200
201
202
203
            {
                update_key(ctx, offset, 0);
            }
            
            crypt_lfsr64_xor_uint64(dst, src, ctx, 0);
            dst += 8;
            src += 8;
            offset += 8;
            length -= 8;
        }
204
205
    }
    
206
    //trace_write(iocrypt_trace_ctx, "crypt_lfsr64_encrypt: (length >= 8) dst 0x%08X, src 0x%08X, length: 0x%08X, offset: 0x%08X", dst, src, length, offset);
207
208
209
    /* processing loop for 64 bit writes at odd file offsets. this loop has to do an extra lookup */
    while(length >= 8)
    {
210
        uint32_t block_remain = offset % blocksize;
211
        
212
        if(block_remain == 0)
213
        {
214
            update_key(ctx, offset, 0);
215
        }
216
        else if((block_remain + 8) > blocksize)
217
        {
218
            if(length < 8)
219
220
221
222
            {
                /* okay, do the rest with per-byte crypts */
                break;
            }
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
            
            for(int local_pos = 0; local_pos < 8; local_pos++)
            {
                if((offset % blocksize) == 0)
                {
                    update_key(ctx, offset, 0);
                }
                
                crypt_lfsr64_xor_uint8(dst, src, ctx, offset);
                dst += 1;
                src += 1;
                offset += 1;
                length -= 1;                
            }
            continue;
238
239
240
241
242
243
244
        }
        
        crypt_lfsr64_xor_uint64(dst, src, ctx, offset);
        dst += 8;
        src += 8;
        offset += 8;
        length -= 8;
245
246
    }
    
247
    //trace_write(iocrypt_trace_ctx, "crypt_lfsr64_encrypt: (length > 0) dst 0x%08X, src 0x%08X, length: 0x%08X, offset: 0x%08X", dst, src, length, offset);
248
    /* do the rest, very slow */
249
250
    while(length > 0)
    {
251
        uint32_t block_remain = offset % blocksize;
252
253
254
255
256
257
        
        if(block_remain == 0)
        {
            update_key(ctx, offset, 0);
        }
        
258
259
260
261
262
263
        crypt_lfsr64_xor_uint8(dst, src, ctx, offset);
        dst += 1;
        src += 1;
        offset += 1;
        length -= 1;
    }
264
    
265
266
267
268
269
270
271
272
    if((uint32_t)dst_in + in_length != (uint32_t)dst)
    {
        trace_write(iocrypt_trace_ctx, "crypt_lfsr64_encrypt: ADDRESS ERROR dst 0x%08X/0x%08X, src 0x%08X/0x%08X, length: 0x%08X, offset: 0x%08X", dst, dst_in, src, src_in, length, offset);
    }
    if((uint32_t)src_in + in_length != (uint32_t)src)
    {
        trace_write(iocrypt_trace_ctx, "crypt_lfsr64_encrypt: ADDRESS ERROR dst 0x%08X/0x%08X, src 0x%08X/0x%08X, length: 0x%08X, offset: 0x%08X", dst, dst_in, src, src_in, length, offset);
    }
273
274
    
    return in_length;
275
276
277
}

/* using a symmetric cipher, both encryption and decryption are the same */
g3gg0's avatar
g3gg0 committed
278
static uint32_t crypt_lfsr64_decrypt(crypt_priv_t *priv, uint8_t *dst, uint8_t *src, uint32_t length, uint32_t offset)
279
{
g3gg0's avatar
g3gg0 committed
280
    return crypt_lfsr64_encrypt(priv, dst, src, length, offset);
281
282
}

283
/* set encryption blocksize for this context */
g3gg0's avatar
g3gg0 committed
284
static void crypt_lfsr64_set_blocksize(crypt_priv_t *priv, uint32_t size)
285
{
g3gg0's avatar
g3gg0 committed
286
    lfsr64_ctx_t *ctx = (lfsr64_ctx_t *)priv;
287
288
    
    ctx->blocksize = size;
289
290
}

291
/* not much to do here, just free the previously allocated structure */
g3gg0's avatar
g3gg0 committed
292
static void crypt_lfsr64_deinit(crypt_priv_t *priv)
293
{
g3gg0's avatar
g3gg0 committed
294
295
296
    lfsr64_ctx_t *ctx = (lfsr64_ctx_t *)priv;
    
    if(ctx)
297
    {
g3gg0's avatar
g3gg0 committed
298
        free(ctx);
299
300
301
    }
}

302
/* reset operation is not implemented yet */
g3gg0's avatar
g3gg0 committed
303
static void crypt_lfsr64_reset(crypt_priv_t *priv)
304
{
g3gg0's avatar
g3gg0 committed
305
    lfsr64_ctx_t *ctx = (lfsr64_ctx_t *)priv;
306
    
307
    ctx->current_block = 0;
308
309
310
311
312
313
314
    ctx->lfsr_state = 0;
    ctx->blocksize = crypt_lfsr64_blocksize;
    
    /* setup initial cipher key */
    update_key(ctx, 0, 1); 
}

315
/* allocate and initialize an LFSR64 cipher ctx and save to pointer */
316
void crypt_lfsr64_init(crypt_cipher_t *crypt_ctx, uint64_t password)
317
318
319
320
321
322
323
324
325
326
{
    lfsr64_ctx_t *ctx = malloc(sizeof(lfsr64_ctx_t));
    
    if(!ctx)
    {
        trace_write(iocrypt_trace_ctx, "crypt_lfsr64_init: failed to malloc");
        return;
    }
    
    /* setup cipher ctx */
g3gg0's avatar
g3gg0 committed
327
328
329
330
331
    crypt_ctx->encrypt = &crypt_lfsr64_encrypt;
    crypt_ctx->decrypt = &crypt_lfsr64_decrypt;
    crypt_ctx->deinit = &crypt_lfsr64_deinit;
    crypt_ctx->reset = &crypt_lfsr64_reset;
    crypt_ctx->set_blocksize = &crypt_lfsr64_set_blocksize;
332
    crypt_ctx->priv = ctx;
333
    
334
335
    /* initialize to default values */
    ctx->password = password;
g3gg0's avatar
g3gg0 committed
336
    crypt_lfsr64_reset(ctx);
337
338
339
340
341
    
    trace_write(iocrypt_trace_ctx, "crypt_lfsr64_init: initialized");
}