1 /***************************************************************************
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
27 This viewer plugin is for the encryption/decryption of iPod Nano
28 (2nd generation) firmware images using the hardware AES crypto unit
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.
42 static void aes_encrypt(void* data
, uint32_t size
)
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);
62 for (i
= 0; i
< 4; i
++)
63 ((uint32_t*)data
)[ptr
+ i
] ^= ((uint32_t*)data
)[ptr
+ i
- 4];
67 while ((AESSTATUS
& 6) == 0);
73 static void aes_decrypt(void* data
, uint32_t size
)
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);
95 while ((AESSTATUS
& 6) == 0);
97 for (i
= 0; i
< 4; i
++)
98 ((uint32_t*)data
)[ptr
+ i
] ^= ((uint32_t*)data
)[ptr
+ i
- 4];
104 static void calc_hash(uint32_t* data
, uint32_t size
, uint32_t* result
)
111 for (ptr
= 0; ptr
< (size
>> 2); ptr
+= 0x10)
113 for (i
= 0; i
< 0x10; i
++) HASHDATAIN
[i
] = data
[ptr
+ i
];
116 while ((HASHCTRL
& 1) != 0);
118 for (i
= 0; i
< 5; i
++) result
[i
] = HASHRESULT
[i
];
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;
136 static uint32_t calc_checksum(uint32_t sum
, unsigned char* buf
, int len
)
140 for (i
= 0; i
< len
; i
++) {
147 enum plugin_status
plugin_start(const void* parameter
)
156 uint32_t hash
[0x200];
157 char outputfilename
[MAX_PATH
];
159 fd
= rb
->open(parameter
,O_RDONLY
);
162 rb
->splash(HZ
*2, "Cannot open file");
166 length
= rb
->filesize(fd
);
169 rb
->splash(HZ
*2, "File too small");
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 */
179 if (length
> buf_size
) {
180 rb
->splash(HZ
*2, "File too big");
184 n
= rb
->read(fd
, buf
, length
);
186 rb
->splash(HZ
*2, "Cannot read file");
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");
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");
208 if (n
+ 2 > MAX_PATH
) {
209 rb
->splash(HZ
*2, "Filename too long");
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
));
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);
252 rb
->splash(HZ
*2, "Could not open output file");
256 n
= rb
->write(fd
, buf
, 8);
257 n
= rb
->write(fd
, hash
, sizeof(hash
));
258 n
= rb
->write(fd
, buf
+ 2, size
);
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");
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 */
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
));
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");
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);
313 rb
->splash(HZ
*2, "Could not open output file");
317 n
= rb
->write(fd
, buf
, 8);
318 n
= rb
->write(fd
, &buf
[0x202], size
);
322 rb
->splash(HZ
*2,"Invalid input file");