diff --git a/apps/filetypes.c b/apps/filetypes.c index ffa7161693..ed3d938fe9 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -111,6 +111,9 @@ static const struct filetype inbuilt_filetypes[] = { #ifdef BOOTFILE_EXT { BOOTFILE_EXT, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, #endif +#ifdef BOOTFILE_EXT2 + { BOOTFILE_EXT2, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, +#endif }; void tree_get_filetypes(const struct filetype** types, int* count) diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 4cfc3a8b0f..a15564d0be 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -29,6 +29,10 @@ firmware_flash.c rockbox_flash.c #endif /* CONFIG_CPU */ +#if defined(IPOD_NANO2G) && !defined(SIMULATOR) +crypt_firmware.c +#endif + #if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING) && \ (defined(HAVE_LINE_IN) || defined(HAVE_MIC_IN)) pitch_detector.c diff --git a/apps/plugins/crypt_firmware.c b/apps/plugins/crypt_firmware.c new file mode 100644 index 0000000000..6bfeeabe00 --- /dev/null +++ b/apps/plugins/crypt_firmware.c @@ -0,0 +1,346 @@ +/*************************************************************************** + * + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * $Id: $ + * + * Rockbox plugin copyright (C) 2009 Dave Chapman. + * Based on encryption code (C) 2009 Michael Sparmann + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* + + This viewer plugin is for the encryption/decryption of iPod Nano + (2nd generation) firmware images using the hardware AES crypto unit + in such devices. + + Encrypted images are stored with the modelname "nn2x" and extension + ".ipodx" Unencrypted images use "nn2g" and ".ipod". + + Heavily based on Payloads/CryptFirmware/main.c from iBugger. + + The (C) from that file is as follows: + + Copyright 2009 TheSeven + + This file is part of TheSeven's iBugger. + + TheSeven's iBugger 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. + + TheSeven's iBugger 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 TheSeven's iBugger. If not, see . + +*/ + +#include "plugin.h" + +PLUGIN_HEADER + +static void aes_encrypt(void* data, uint32_t size) +{ + uint32_t ptr, i; + uint32_t go = 1; + PWRCONEXT &= ~0x400; + AESTYPE = 1; + AESUNKREG0 = 1; + AESUNKREG0 = 0; + AESCONTROL = 1; + AESKEYLEN = 9; + AESOUTSIZE = size; + AESAUXSIZE = 0x10; + AESINSIZE = 0x10; + AESSIZE3 = 0x10; + for (ptr = 0; ptr < (size >> 2); ptr += 4) + { + AESOUTADDR = (uint32_t)data + (ptr << 2); + AESINADDR = (uint32_t)data + (ptr << 2); + AESAUXADDR = (uint32_t)data + (ptr << 2); + if (ptr != 0) + for (i = 0; i < 4; i++) + ((uint32_t*)data)[ptr + i] ^= ((uint32_t*)data)[ptr + i - 4]; + AESSTATUS = 6; + AESGO = go; + go = 3; + while ((AESSTATUS & 6) == 0); + } + AESCONTROL = 0; + PWRCONEXT |= 0x400; +} + +static void aes_decrypt(void* data, uint32_t size) +{ + uint32_t ptr, i; + uint32_t go = 1; + PWRCONEXT &= ~0x400; + AESTYPE = 1; + AESUNKREG0 = 1; + AESUNKREG0 = 0; + AESCONTROL = 1; + AESKEYLEN = 8; + AESOUTSIZE = size; + AESAUXSIZE = 0x10; + AESINSIZE = 0x10; + AESSIZE3 = 0x10; + for (ptr = (size >> 2) - 4; ; ptr -= 4) + { + AESOUTADDR = (uint32_t)data + (ptr << 2); + AESINADDR = (uint32_t)data + (ptr << 2); + AESAUXADDR = (uint32_t)data + (ptr << 2); + AESSTATUS = 6; + AESGO = go; + go = 3; + while ((AESSTATUS & 6) == 0); + if (ptr == 0) break; + for (i = 0; i < 4; i++) + ((uint32_t*)data)[ptr + i] ^= ((uint32_t*)data)[ptr + i - 4]; + } + AESCONTROL = 0; + PWRCONEXT |= 0x400; +} + +static void calc_hash(uint32_t* data, uint32_t size, uint32_t* result) +{ + uint32_t ptr, i; + uint32_t ctrl = 2; + + PWRCONEXT &= ~0x4; + + for (ptr = 0; ptr < (size >> 2); ptr += 0x10) + { + for (i = 0; i < 0x10; i++) HASHDATAIN[i] = data[ptr + i]; + HASHCTRL = ctrl; + ctrl = 0xA; + while ((HASHCTRL & 1) != 0); + } + for (i = 0; i < 5; i ++) result[i] = HASHRESULT[i]; + + PWRCONEXT |= 0x4; +} + +static uint32_t get_uint32be(unsigned char* buf) +{ + return (uint32_t)((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]); +} + +static void put_uint32be(unsigned char* buf, uint32_t x) +{ + buf[0] = (x & 0xff000000) >> 24; + buf[1] = (x & 0xff0000) >> 16; + buf[2] = (x & 0xff00) >> 8; + buf[3] = x & 0xff; +} + +static uint32_t calc_checksum(uint32_t sum, unsigned char* buf, int len) +{ + int i; + + for (i = 0; i < len ; i++) { + sum += buf[i]; + } + + return sum; +} + +enum plugin_status plugin_start(const void* parameter) +{ + int fd; + int length; + int n; + ssize_t buf_size; + uint32_t* buf; + int size; + uint32_t sum; + uint32_t hash[0x200]; + char outputfilename[MAX_PATH]; + + fd = rb->open(parameter,O_RDONLY); + + if (fd < 0) { + rb->splash(HZ*2, "Cannot open file"); + return PLUGIN_ERROR; + } + + length = rb->filesize(fd); + + if (length < 12) { + rb->splash(HZ*2, "File too small"); + return PLUGIN_ERROR; + } + + if (length > buf_size) { + rb->splash(HZ*2, "File too big"); + return PLUGIN_ERROR; + } + + /* Get the audio buffer */ + buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); + + /* Use uncached alias for buf - equivalent to buf |= 0x40000000 */ + buf += 0x10000000; + + n = rb->read(fd, buf, length); + if (n < length) { + rb->splash(HZ*2, "Cannot read file"); + return PLUGIN_ERROR; + } + rb->close(fd); + + size = length - 8; /* Size of firmware image */ + + if (calc_checksum(MODEL_NUMBER, (unsigned char*)(buf + 2), size) != + get_uint32be((unsigned char*)buf)) { + rb->splash(HZ*2, "Bad checksum in input file"); + return PLUGIN_ERROR; + } + + n = rb->strlen(parameter); + if (memcmp(buf+1,"nn2g",4)==0) { + /* Encrypting - Input file should be .ipod, output file is .ipodx */ + + if ((n < 6) || (rb->strcmp(parameter+n-5,".ipod") != 0)) { + rb->splash(HZ*2, "Input filename must be .ipod"); + return PLUGIN_ERROR; + } + + if (n + 2 > MAX_PATH) { + rb->splash(HZ*2, "Filename too long"); + return PLUGIN_ERROR; + } + + size = (size + 0x3f) & ~0x3f; /* Pad to multiple of 64 bytes */ + if (size > (length - 8)) { + rb->memset(&buf[length/4], 0, size - (length - 8)); + } + + rb->strlcpy(outputfilename, parameter, MAX_PATH); + outputfilename[n] = 'x'; + outputfilename[n+1] = 0; + + /* Everything is OK, now do the encryption */ + + /* 1 - Calculate hashes */ + + rb->memset(hash, 0, sizeof(hash)); + + hash[1] = 2; + hash[2] = 1; + hash[3] = 0x40; + hash[5] = size; + + calc_hash(buf + 2, size, &hash[7]); + calc_hash(hash, 0x200, &hash[0x75]); + + /* 2 - Do the encryption */ + + rb->splash(0, "Encrypting..."); + aes_encrypt(buf + 2, size); + + /* 3 - Update the Rockbox header */ + + sum = calc_checksum(MODEL_NUMBER, (unsigned char*)hash, sizeof(hash)); + sum = calc_checksum(sum, (unsigned char*)(buf + 2), size); + put_uint32be((unsigned char*)buf, sum); + memcpy(buf + 1, "nn2x", 4); + + /* 4 - Write to disk */ + fd = rb->open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC); + + if (fd < 0) { + rb->splash(HZ*2, "Could not open output file"); + return PLUGIN_ERROR; + } + + n = rb->write(fd, buf, 8); + n = rb->write(fd, hash, sizeof(hash)); + n = rb->write(fd, buf + 2, size); + + rb->close(fd); + } else if (memcmp(buf + 1,"nn2x",4)==0) { + /* Decrypting - Input file should be .ipodx, output file is .ipod */ + + if ((n < 7) || (rb->strcmp(parameter+n-6,".ipodx") != 0)) { + rb->splash(HZ*2, "Input filename must be .ipodx"); + return PLUGIN_ERROR; + } + + rb->strlcpy(outputfilename, parameter, MAX_PATH); + outputfilename[n-1] = 0; /* Remove "x" at end of filename */ + + /* Everything is OK, now do the decryption */ + + size -= 0x800; /* Remove hash size from firmware size */ + + /* 1 - Decrypt */ + + rb->splash(0, "Decrypting..."); + + aes_decrypt(&buf[0x202], size); + + /* 2 - Calculate hashes to verify decryption */ + + rb->lcd_clear_display(); + rb->splash(0, "Calculating hash..."); + + rb->memset(hash, 0, sizeof(hash)); + + hash[1] = 2; + hash[2] = 1; + hash[3] = 0x40; + hash[5] = size; + + calc_hash(&buf[0x202], size, &hash[7]); + calc_hash(hash, 0x200, &hash[0x75]); + + if ((memcmp(hash + 7, buf + 9, 20) != 0) || + (memcmp(hash + 75, buf + 77, 20) != 0)) { + rb->splash(HZ*2, "Decryption failed - bad hash"); + return PLUGIN_ERROR; + } + + /* 3 - Update the Rockbox header */ + + sum = calc_checksum(MODEL_NUMBER, (unsigned char*)(&buf[0x202]), size); + put_uint32be((unsigned char*)buf, sum); + memcpy(buf + 1, "nn2g", 4); + + /* 4 - Write to disk */ + fd = rb->open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC); + + if (fd < 0) { + rb->splash(HZ*2, "Could not open output file"); + return PLUGIN_ERROR; + } + + n = rb->write(fd, buf, 8); + n = rb->write(fd, &buf[0x202], size); + + rb->close(fd); + } else { + rb->splash(HZ*2,"Invalid input file"); + return PLUGIN_ERROR; + } + + return PLUGIN_OK; +} diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config index a572c6abfa..e3b15fe116 100644 --- a/apps/plugins/viewers.config +++ b/apps/plugins/viewers.config @@ -58,3 +58,5 @@ link,viewers/shortcuts_view,- *,viewers/shortcuts_append,- *,apps/md5sum,- lua,viewers/lua,- +ipod,viewers/crypt_firmware,- +ipodx,viewers/crypt_firmware,- diff --git a/firmware/export/config-ipodnano2g.h b/firmware/export/config-ipodnano2g.h index 641e88860b..d9c5cbf3ea 100644 --- a/firmware/export/config-ipodnano2g.h +++ b/firmware/export/config-ipodnano2g.h @@ -159,6 +159,9 @@ #define BOOTFILE "rockbox." BOOTFILE_EXT #define BOOTDIR "/.rockbox" +/* Alternative bootfile extension - this is for encrypted images */ +#define BOOTFILE_EXT2 "ipodx" + #define BOOTLOADER_ENTRYPOINT 0x001F0000 #define FLASH_ENTRYPOINT 0x00001000 #define FLASH_MAGIC 0xfbfbfbf1 diff --git a/firmware/export/s5l8700.h b/firmware/export/s5l8700.h index f652a62a2e..f9e015baff 100644 --- a/firmware/export/s5l8700.h +++ b/firmware/export/s5l8700.h @@ -120,6 +120,7 @@ #define RSTSR (*(REG32_PTR_T)(0x3C500034)) /* Reset status register */ #define DSPCLKMD (*(REG32_PTR_T)(0x3C500038)) /* DSP clock mode register */ #define CLKCON2 (*(REG32_PTR_T)(0x3C50003C)) /* clock control register 2 */ +#define PWRCONEXT (*(REG32_PTR_T)(0x3C500040)) /* 06. INTERRUPT CONTROLLER UNIT */ #define SRCPND (*(REG32_PTR_T)(0x39C00000)) /* Indicates the interrupt request status. */ @@ -670,3 +671,28 @@ #define REG_ONE (*(REG32_PTR_T)(0x3D100000)) /* Receive the first 32 bits from a fuse box */ #define REG_TWO (*(REG32_PTR_T)(0x3D100004)) /* Receive the other 8 bits from a fuse box */ + +/* Hardware AES crypto unit - S5L8701 only */ +#if CONFIG_CPU==S5L8701 + +#define ICONSRCPND (*(REG32_PTR_T)(0x39C00000)) +#define ICONINTPND (*(REG32_PTR_T)(0x39C00010)) +#define AESCONTROL (*(REG32_PTR_T)(0x39800000)) +#define AESGO (*(REG32_PTR_T)(0x39800004)) +#define AESUNKREG0 (*(REG32_PTR_T)(0x39800008)) +#define AESSTATUS (*(REG32_PTR_T)(0x3980000C)) +#define AESUNKREG1 (*(REG32_PTR_T)(0x39800010)) +#define AESKEYLEN (*(REG32_PTR_T)(0x39800014)) +#define AESOUTSIZE (*(REG32_PTR_T)(0x39800018)) +#define AESOUTADDR (*(REG32_PTR_T)(0x39800020)) +#define AESINSIZE (*(REG32_PTR_T)(0x39800024)) +#define AESINADDR (*(REG32_PTR_T)(0x39800028)) +#define AESAUXSIZE (*(REG32_PTR_T)(0x3980002C)) +#define AESAUXADDR (*(REG32_PTR_T)(0x39800030)) +#define AESSIZE3 (*(REG32_PTR_T)(0x39800034)) +#define AESTYPE (*(REG32_PTR_T)(0x3980006C)) +#define HASHCTRL (*(REG32_PTR_T)(0x3C600000)) +#define HASHRESULT ((REG32_PTR_T)(0x3C600020)) +#define HASHDATAIN ((REG32_PTR_T)(0x3C600040)) + +#endif /* CONFIG_CPU==S5L8701 */