Make open() posix compliant api-wise. A few calls (those with O_CREAT) need the addit...
[kugel-rb.git] / apps / plugins / crypt_firmware.c
blob051a3d1bd98960e5cd8e54f6a7d8570a386a1b23
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
10 * $Id$
12 * Rockbox plugin copyright (C) 2009 Dave Chapman.
13 * Based on encryption code (C) 2009 Michael Sparmann
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
25 /*
27 This viewer plugin is for the encryption/decryption of iPod Nano
28 (2nd generation) firmware images using the hardware AES crypto unit
29 in such devices.
31 Encrypted images are stored with the modelname "nn2x" and extension
32 ".ipodx" Unencrypted images use "nn2g" and ".ipod".
34 Heavily based on Payloads/CryptFirmware/main.c from iBugger.
38 #include "plugin.h"
40 PLUGIN_HEADER
42 static void aes_encrypt(void* data, uint32_t size)
44 uint32_t ptr, i;
45 uint32_t go = 1;
46 PWRCONEXT &= ~0x400;
47 AESTYPE = 1;
48 AESUNKREG0 = 1;
49 AESUNKREG0 = 0;
50 AESCONTROL = 1;
51 AESKEYLEN = 9;
52 AESOUTSIZE = size;
53 AESAUXSIZE = 0x10;
54 AESINSIZE = 0x10;
55 AESSIZE3 = 0x10;
56 for (ptr = 0; ptr < (size >> 2); ptr += 4)
58 AESOUTADDR = (uint32_t)data + (ptr << 2);
59 AESINADDR = (uint32_t)data + (ptr << 2);
60 AESAUXADDR = (uint32_t)data + (ptr << 2);
61 if (ptr != 0)
62 for (i = 0; i < 4; i++)
63 ((uint32_t*)data)[ptr + i] ^= ((uint32_t*)data)[ptr + i - 4];
64 AESSTATUS = 6;
65 AESGO = go;
66 go = 3;
67 while ((AESSTATUS & 6) == 0);
69 AESCONTROL = 0;
70 PWRCONEXT |= 0x400;
73 static void aes_decrypt(void* data, uint32_t size)
75 uint32_t ptr, i;
76 uint32_t go = 1;
77 PWRCONEXT &= ~0x400;
78 AESTYPE = 1;
79 AESUNKREG0 = 1;
80 AESUNKREG0 = 0;
81 AESCONTROL = 1;
82 AESKEYLEN = 8;
83 AESOUTSIZE = size;
84 AESAUXSIZE = 0x10;
85 AESINSIZE = 0x10;
86 AESSIZE3 = 0x10;
87 for (ptr = (size >> 2) - 4; ; ptr -= 4)
89 AESOUTADDR = (uint32_t)data + (ptr << 2);
90 AESINADDR = (uint32_t)data + (ptr << 2);
91 AESAUXADDR = (uint32_t)data + (ptr << 2);
92 AESSTATUS = 6;
93 AESGO = go;
94 go = 3;
95 while ((AESSTATUS & 6) == 0);
96 if (ptr == 0) break;
97 for (i = 0; i < 4; i++)
98 ((uint32_t*)data)[ptr + i] ^= ((uint32_t*)data)[ptr + i - 4];
100 AESCONTROL = 0;
101 PWRCONEXT |= 0x400;
104 static void calc_hash(uint32_t* data, uint32_t size, uint32_t* result)
106 uint32_t ptr, i;
107 uint32_t ctrl = 2;
109 PWRCONEXT &= ~0x4;
111 for (ptr = 0; ptr < (size >> 2); ptr += 0x10)
113 for (i = 0; i < 0x10; i++) HASHDATAIN[i] = data[ptr + i];
114 HASHCTRL = ctrl;
115 ctrl = 0xA;
116 while ((HASHCTRL & 1) != 0);
118 for (i = 0; i < 5; i ++) result[i] = HASHRESULT[i];
120 PWRCONEXT |= 0x4;
123 static uint32_t get_uint32be(unsigned char* buf)
125 return (uint32_t)((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
128 static void put_uint32be(unsigned char* buf, uint32_t x)
130 buf[0] = (x & 0xff000000) >> 24;
131 buf[1] = (x & 0xff0000) >> 16;
132 buf[2] = (x & 0xff00) >> 8;
133 buf[3] = x & 0xff;
136 static uint32_t calc_checksum(uint32_t sum, unsigned char* buf, int len)
138 int i;
140 for (i = 0; i < len ; i++) {
141 sum += buf[i];
144 return sum;
147 enum plugin_status plugin_start(const void* parameter)
149 int fd;
150 int length;
151 int n;
152 ssize_t buf_size;
153 uint32_t* buf;
154 int size;
155 uint32_t sum;
156 uint32_t hash[0x200];
157 char outputfilename[MAX_PATH];
159 fd = rb->open(parameter,O_RDONLY);
161 if (fd < 0) {
162 rb->splash(HZ*2, "Cannot open file");
163 return PLUGIN_ERROR;
166 length = rb->filesize(fd);
168 if (length < 12) {
169 rb->splash(HZ*2, "File too small");
170 return PLUGIN_ERROR;
173 /* Get the audio buffer */
174 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
176 /* Use uncached alias for buf - equivalent to buf |= 0x40000000 */
177 buf += 0x10000000;
179 if (length > buf_size) {
180 rb->splash(HZ*2, "File too big");
181 return PLUGIN_ERROR;
184 n = rb->read(fd, buf, length);
185 if (n < length) {
186 rb->splash(HZ*2, "Cannot read file");
187 return PLUGIN_ERROR;
189 rb->close(fd);
191 size = length - 8; /* Size of firmware image */
193 if (calc_checksum(MODEL_NUMBER, (unsigned char*)(buf + 2), size) !=
194 get_uint32be((unsigned char*)buf)) {
195 rb->splash(HZ*2, "Bad checksum in input file");
196 return PLUGIN_ERROR;
199 n = rb->strlen(parameter);
200 if (memcmp(buf+1,"nn2g",4)==0) {
201 /* Encrypting - Input file should be .ipod, output file is .ipodx */
203 if ((n < 6) || (rb->strcmp(parameter+n-5,".ipod") != 0)) {
204 rb->splash(HZ*2, "Input filename must be .ipod");
205 return PLUGIN_ERROR;
208 if (n + 2 > MAX_PATH) {
209 rb->splash(HZ*2, "Filename too long");
210 return PLUGIN_ERROR;
213 size = (size + 0x3f) & ~0x3f; /* Pad to multiple of 64 bytes */
214 if (size > (length - 8)) {
215 rb->memset(&buf[length/4], 0, size - (length - 8));
218 rb->strlcpy(outputfilename, parameter, MAX_PATH);
219 outputfilename[n] = 'x';
220 outputfilename[n+1] = 0;
222 /* Everything is OK, now do the encryption */
224 /* 1 - Calculate hashes */
226 rb->memset(hash, 0, sizeof(hash));
228 hash[1] = 2;
229 hash[2] = 1;
230 hash[3] = 0x40;
231 hash[5] = size;
233 calc_hash(buf + 2, size, &hash[7]);
234 calc_hash(hash, 0x200, &hash[0x75]);
236 /* 2 - Do the encryption */
238 rb->splash(0, "Encrypting...");
239 aes_encrypt(buf + 2, size);
241 /* 3 - Update the Rockbox header */
243 sum = calc_checksum(MODEL_NUMBER, (unsigned char*)hash, sizeof(hash));
244 sum = calc_checksum(sum, (unsigned char*)(buf + 2), size);
245 put_uint32be((unsigned char*)buf, sum);
246 memcpy(buf + 1, "nn2x", 4);
248 /* 4 - Write to disk */
249 fd = rb->open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC, 0666);
251 if (fd < 0) {
252 rb->splash(HZ*2, "Could not open output file");
253 return PLUGIN_ERROR;
256 n = rb->write(fd, buf, 8);
257 n = rb->write(fd, hash, sizeof(hash));
258 n = rb->write(fd, buf + 2, size);
260 rb->close(fd);
261 } else if (memcmp(buf + 1,"nn2x",4)==0) {
262 /* Decrypting - Input file should be .ipodx, output file is .ipod */
264 if ((n < 7) || (rb->strcmp(parameter+n-6,".ipodx") != 0)) {
265 rb->splash(HZ*2, "Input filename must be .ipodx");
266 return PLUGIN_ERROR;
269 rb->strlcpy(outputfilename, parameter, MAX_PATH);
270 outputfilename[n-1] = 0; /* Remove "x" at end of filename */
272 /* Everything is OK, now do the decryption */
274 size -= 0x800; /* Remove hash size from firmware size */
276 /* 1 - Decrypt */
278 rb->splash(0, "Decrypting...");
280 aes_decrypt(&buf[0x202], size);
282 /* 2 - Calculate hashes to verify decryption */
284 rb->lcd_clear_display();
285 rb->splash(0, "Calculating hash...");
287 rb->memset(hash, 0, sizeof(hash));
289 hash[1] = 2;
290 hash[2] = 1;
291 hash[3] = 0x40;
292 hash[5] = size;
294 calc_hash(&buf[0x202], size, &hash[7]);
295 calc_hash(hash, 0x200, &hash[0x75]);
297 if ((memcmp(hash + 7, buf + 9, 20) != 0) ||
298 (memcmp(hash + 75, buf + 77, 20) != 0)) {
299 rb->splash(HZ*2, "Decryption failed - bad hash");
300 return PLUGIN_ERROR;
303 /* 3 - Update the Rockbox header */
305 sum = calc_checksum(MODEL_NUMBER, (unsigned char*)(&buf[0x202]), size);
306 put_uint32be((unsigned char*)buf, sum);
307 memcpy(buf + 1, "nn2g", 4);
309 /* 4 - Write to disk */
310 fd = rb->open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC, 0666);
312 if (fd < 0) {
313 rb->splash(HZ*2, "Could not open output file");
314 return PLUGIN_ERROR;
317 n = rb->write(fd, buf, 8);
318 n = rb->write(fd, &buf[0x202], size);
320 rb->close(fd);
321 } else {
322 rb->splash(HZ*2,"Invalid input file");
323 return PLUGIN_ERROR;
326 return PLUGIN_OK;