Add functions to read and write the AUPD (flash update) image.  "--read-aupd aupd.bin" will read (and decrypt) the AUPD image, and "--write-aupd aupd.bin" will write (and encrypt) an image.  Also fix a bug in the "diskmove" function which corrupted the AUPD image when a bootloader was installed.  So in order to manipulate the aupd image, you need to restore a clean firmware partition, and install the bootloader with this version of ipodpatcher.  Decryption functions based on the description and sample code at http://ipodlinux.org/Flash_Decryption

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14644 a1c6a512-1295-4272-9138-f99709370657
diff --git a/rbutil/ipodpatcher/Makefile b/rbutil/ipodpatcher/Makefile
index f65234a..f76715b 100644
--- a/rbutil/ipodpatcher/Makefile
+++ b/rbutil/ipodpatcher/Makefile
@@ -22,7 +22,7 @@
 NATIVECC = gcc
 CC = $(CROSS)gcc
 
-SRC = main.c ipodpatcher.c fat32format.c parttypes.h
+SRC = main.c ipodpatcher.c fat32format.c parttypes.h arc4.c
 
 all: $(OUTPUT)
 
diff --git a/rbutil/ipodpatcher/arc4.c b/rbutil/ipodpatcher/arc4.c
new file mode 100644
index 0000000..75b1862
--- /dev/null
+++ b/rbutil/ipodpatcher/arc4.c
@@ -0,0 +1,108 @@
+/*
+ * arc4.c
+ * Release $Name: MATRIXSSL_1_8_3_OPEN $
+ *
+ * ARC4 stream cipher implementation
+ */
+/*
+ * Copyright (c) PeerSec Networks, 2002-2007. All Rights Reserved.
+ * The latest version of this code is available at http://www.matrixssl.org
+ *
+ * This software is open source; 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 General Public License does NOT permit incorporating this software 
+ * into proprietary programs.  If you are unable to comply with the GPL, a 
+ * commercial license for this software may be purchased from PeerSec Networks
+ * at http://www.peersec.com
+ * 
+ * This program is distributed in 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+/******************************************************************************/
+
+#include "arc4.h"
+
+/*
+    Some accounts, such as O'Reilly's Secure Programming Cookbook say that no 
+    more than 2^30 bytes should be processed without rekeying, so we 
+    enforce that limit here.  FYI, this is equal to 1GB of data transferred.
+*/
+#define ARC4_MAX_BYTES 0x40000000
+
+/******************************************************************************/
+/*
+    SSL_RSA_WITH_RC4_* cipher callbacks
+ */
+void matrixArc4Init(struct rc4_key_t *ctx, unsigned char *key, int32_t keylen)
+{
+    unsigned char    index1, index2, tmp, *state;
+    int16_t            counter;
+
+    ctx->byteCount = 0;
+    state = &ctx->state[0];
+
+    for (counter = 0; counter < 256; counter++) {
+        state[counter] = (unsigned char)counter;
+    }
+    ctx->x = 0;
+    ctx->y = 0;
+    index1 = 0;
+    index2 = 0;
+
+    for (counter = 0; counter < 256; counter++) {
+        index2 = (key[index1] + state[counter] + index2) & 0xff;
+
+        tmp = state[counter];
+        state[counter] = state[index2];
+        state[index2] = tmp;
+
+        index1 = (index1 + 1) % keylen;
+    }
+}
+
+int32_t matrixArc4(struct rc4_key_t *ctx, unsigned char *in,
+                   unsigned char *out, int32_t len)
+{
+    unsigned char    x, y, *state, xorIndex, tmp;
+    int   counter;     /* NOTE BY DAVE CHAPMAN: This was a short in
+			  the original code, which caused a segfault
+			  when attempting to process data > 32767
+			  bytes. */
+
+    ctx->byteCount += len;
+    if (ctx->byteCount > ARC4_MAX_BYTES) {
+        return -1;
+    }
+
+    x = ctx->x;
+    y = ctx->y;
+    state = &ctx->state[0];
+    for (counter = 0; counter < len; counter++) {
+        x = (x + 1) & 0xff;
+        y = (state[x] + y) & 0xff;
+
+        tmp = state[x];
+        state[x] = state[y];
+        state[y] = tmp;
+
+        xorIndex = (state[x] + state[y]) & 0xff;
+
+        tmp = in[counter];
+        tmp ^= state[xorIndex];
+        out[counter] = tmp;
+    }
+    ctx->x = x;
+    ctx->y = y;
+    return len;
+}
+
+/*****************************************************************************/
diff --git a/rbutil/ipodpatcher/arc4.h b/rbutil/ipodpatcher/arc4.h
new file mode 100644
index 0000000..8bff0e2
--- /dev/null
+++ b/rbutil/ipodpatcher/arc4.h
@@ -0,0 +1,47 @@
+/*
+   arc4.h - based on matrixssl-1-8-3-open
+
+*/
+
+/*
+ *  Copyright (c) PeerSec Networks, 2002-2007. All Rights Reserved.
+ *  The latest version of this code is available at http://www.matrixssl.org
+ *
+ *  This software is open source; 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 General Public License does NOT permit incorporating this software 
+ *  into proprietary programs.  If you are unable to comply with the GPL, a 
+ *  commercial license for this software may be purchased from PeerSec Networks
+ *  at http://www.peersec.com
+ *  
+ *  This program is distributed in 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  http://www.gnu.org/copyleft/gpl.html
+ */
+/*****************************************************************************/
+
+#ifndef _ARC4_H
+
+#include <stdint.h>
+
+struct rc4_key_t
+{
+    unsigned char   state[256];
+    uint32_t  byteCount;
+    unsigned char   x;
+    unsigned char   y;
+};
+
+void matrixArc4Init(struct rc4_key_t *ctx, unsigned char *key, int32_t keylen);
+int32_t matrixArc4(struct rc4_key_t *ctx, unsigned char *in,
+                   unsigned char *out, int32_t len);
+
+#endif
diff --git a/rbutil/ipodpatcher/ipodpatcher.c b/rbutil/ipodpatcher/ipodpatcher.c
index 2655c57..08ba926 100644
--- a/rbutil/ipodpatcher/ipodpatcher.c
+++ b/rbutil/ipodpatcher/ipodpatcher.c
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <inttypes.h>
+#include <stdbool.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
@@ -41,6 +42,10 @@
 #include "ipodvideo.h"
 #endif
 
+#ifndef RBUTIL
+#include "arc4.h"
+#endif
+
 extern int verbose;
 
 unsigned char* sectorbuf;
@@ -392,7 +397,7 @@
     int chunksize;
     int n;
 
-    src_start = ipod->ipod_directory[1].devOffset + ipod->sector_size;
+    src_start = ipod->ipod_directory[1].devOffset;
     src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size + 
               ipod->ipod_directory[ipod->nimages-1].len + 
               (ipod->sector_size-1)) & ~(ipod->sector_size-1);
@@ -575,7 +580,7 @@
              ipod->ipod_directory[1].devOffset) {
             fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n");
             delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength
-                    - ipod->ipod_directory[1].devOffset;
+                    - ipod->ipod_directory[1].devOffset + ipod->sector_size;
 
             if (diskmove(ipod, delta) < 0) {
                 fprintf(stderr,"[ERR]  Image movement failed.\n");
@@ -1373,3 +1378,343 @@
 
     return 0;
 }
+
+#ifndef RBUTIL
+
+static inline uint32_t getuint32le(unsigned char* buf)
+{
+  int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+  return res;
+}
+
+/* testMarker and GetSecurityBlockKey based on code from BadBlocks and
+   Kingstone, posted at http://ipodlinux.org/Flash_Decryption
+
+*/
+
+static bool testMarker(int marker)
+{
+    int mask, decrypt, temp1, temp2;
+
+    mask = (marker&0xff)|((marker&0xff)<<8)|((marker&0xff)<<16)|((marker&0xff)<<24);
+    decrypt = marker ^ mask;
+    temp1=(int)((unsigned int)decrypt>>24);
+    temp2=decrypt<<8;
+
+    if (temp1==0)
+        return false;
+
+    temp2=(int)((unsigned int)temp2>>24);
+    decrypt=decrypt<<16;
+    decrypt=(int)((unsigned int)decrypt>>24);
+
+    if ((temp1 < temp2) && (temp2 < decrypt))
+    {
+        temp1 = temp1 & 0xf;
+        temp2 = temp2 & 0xf;
+        decrypt = decrypt & 0xf;
+
+        if ((temp1 > temp2) && (temp2 > decrypt) && (decrypt != 0))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+static int GetSecurityBlockKey(unsigned char *data, unsigned char* this_key)
+{
+    int constant = 0x54c3a298;
+    int key=0;
+    int nkeys = 0;
+    int aMarker=0;
+    int pos=0;
+    int c, count;
+    int temp1;
+    static const int offset[8]={0x5,0x25,0x6f,0x69,0x15,0x4d,0x40,0x34};
+
+    for (c = 0; c < 8; c++)
+    {
+        pos = offset[c]*4;
+        aMarker = getuint32le(data + pos);
+
+        if (testMarker(aMarker))
+        {
+            if (c<7)
+                pos =(offset[c+1]*4)+4;
+            else
+                pos =(offset[0]*4)+4;
+
+            key=0;
+
+            temp1=aMarker;
+
+            for (count=0;count<2;count++){
+                int word = getuint32le(data + pos);
+                temp1 = aMarker;
+                temp1 = temp1^word;
+                temp1 = temp1^constant;
+                key = temp1;
+                pos = pos+4;
+            }
+            int r1=0x6f;
+            int r2=0;
+            int r12;
+            int r14;
+            unsigned int r_tmp;
+
+            for (count=2;count<128;count=count+2){
+                r2=getuint32le(data+count*4);
+                r12=getuint32le(data+(count*4)+4);
+                r_tmp=(unsigned int)r12>>16;
+                r14=r2 | ((int)r_tmp);
+                r2=r2&0xffff;
+                r2=r2 | r12;
+                r1=r1^r14;
+                r1=r1+r2;
+            }
+            key=key^r1;
+
+            // Invert key, little endian
+            this_key[0] = key & 0xff;
+            this_key[1] = (key >> 8) & 0xff;
+            this_key[2] = (key >> 16) & 0xff;
+            this_key[3] = (key >> 24) & 0xff;
+            nkeys++;
+        }
+    }
+    return nkeys;
+}
+
+static int find_key(struct ipod_t* ipod, int aupd, unsigned char* key)
+{
+    int n;
+
+    /* Firstly read the security block and find the RC4 key.  This is
+       in the sector preceeding the AUPD image. */
+
+    fprintf(stderr, "[INFO] Reading security block at offset 0x%08x\n",ipod->ipod_directory[aupd].devOffset-ipod->sector_size);
+    if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset-ipod->sector_size) < 0) {
+        return -1;
+    }
+
+    if ((n = ipod_read(ipod, sectorbuf, 512)) < 0) {
+        return -1;
+    }
+
+    n = GetSecurityBlockKey(sectorbuf, key);
+
+    if (n != 1)
+    {
+        fprintf(stderr, "[ERR]  %d keys found in security block, can not continue\n",n);
+        return -1;
+    }
+
+    return 0;
+}
+
+int read_aupd(struct ipod_t* ipod, char* filename)
+{
+    int length;
+    int i;
+    int outfile;
+    int n;
+    int aupd;
+    struct rc4_key_t rc4;
+    unsigned char key[4];
+    unsigned long chksum=0;
+
+    aupd = 0;
+    while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD))
+    {
+        aupd++;
+    }
+
+    if (aupd == ipod->nimages)
+    {
+        fprintf(stderr,"[ERR]  No AUPD image in firmware partition.\n");
+        return -1;
+    }
+
+    length = ipod->ipod_directory[aupd].len;
+
+    fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
+
+    if (find_key(ipod, aupd, key) < 0)
+    {
+       return -1;
+    }
+
+    fprintf(stderr, "[INFO] Decrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]);
+
+    if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) {
+        return -1;
+    }
+
+    i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
+
+    if ((n = ipod_read(ipod,sectorbuf,i)) < 0) {
+        return -1;
+    }
+
+    if (n < i) {
+        fprintf(stderr,"[ERR]  Short read - requested %d bytes, received %d\n",
+                i,n);
+        return -1;
+    }
+
+    /* Perform the decryption - this is standard (A)RC4 */
+    matrixArc4Init(&rc4, key, 4);
+    matrixArc4(&rc4, sectorbuf, sectorbuf, length);
+
+    chksum = 0;
+    for (i = 0; i < (int)length; i++) {
+         /* add 8 unsigned bits but keep a 32 bit sum */
+         chksum += sectorbuf[i];
+    }
+
+    if (chksum != ipod->ipod_directory[aupd].chksum)
+    {
+        fprintf(stderr,"[ERR]  Decryption failed - checksum error\n");
+        return -1;
+    }
+    fprintf(stderr,"[INFO] Decrypted OK (checksum matches header)\n");
+
+    outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
+    if (outfile < 0) {
+        fprintf(stderr,"[ERR]  Couldn't open file %s\n",filename);
+        return -1;
+    }
+
+    n = write(outfile,sectorbuf,length);
+    if (n != length) {
+        fprintf(stderr,"[ERR]  Write error - %d\n",n);
+    }
+    close(outfile);
+
+    return 0;
+}
+
+int write_aupd(struct ipod_t* ipod, char* filename)
+{
+    unsigned int length;
+    int i;
+    int x;
+    int n;
+    int infile;
+    int newsize;
+    int aupd;
+    unsigned long chksum=0;
+    struct rc4_key_t rc4;
+    unsigned char key[4];
+
+    /* First check that the input file is the correct type for this ipod. */
+    infile=open(filename,O_RDONLY);
+    if (infile < 0) {
+        fprintf(stderr,"[ERR]  Couldn't open input file %s\n",filename);
+        return -1;
+    }
+    
+    length = filesize(infile);
+    newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
+
+    fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
+            length,newsize);
+
+    if (newsize > BUFFER_SIZE) {
+        fprintf(stderr,"[ERR]  Input file too big for buffer\n");
+        if (infile >= 0) close(infile);
+        return -1;
+    }
+
+    /* Find aupd image number */
+    aupd = 0;
+    while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD))
+    {
+        aupd++;
+    }
+
+    if (aupd == ipod->nimages)
+    {
+        fprintf(stderr,"[ERR]  No AUPD image in firmware partition.\n");
+        return -1;
+    }
+
+    if (length != ipod->ipod_directory[aupd].len)
+    {
+        fprintf(stderr,"[ERR]  AUPD image (%d bytes) differs in size to %s (%d bytes).\n",
+                       ipod->ipod_directory[aupd].len, filename, length);
+        return -1;
+    }
+
+    if (find_key(ipod, aupd, key) < 0)
+    {
+       return -1;
+    }
+
+    fprintf(stderr, "[INFO] Encrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]);
+
+    /* We now know we have enough space, so write it. */
+
+    fprintf(stderr,"[INFO] Reading input file...\n");
+    n = read(infile,sectorbuf,length);
+    if (n < 0) {
+        fprintf(stderr,"[ERR]  Couldn't read input file\n");
+        close(infile);
+        return -1;
+    }
+    close(infile);
+
+    /* Pad the data with zeros */
+    memset(sectorbuf+length,0,newsize-length);
+
+    /* Calculate the new checksum (before we encrypt) */
+    chksum = 0;
+    for (i = 0; i < (int)length; i++) {
+         /* add 8 unsigned bits but keep a 32 bit sum */
+         chksum += sectorbuf[i];
+    }
+
+    /* Perform the encryption - this is standard (A)RC4 */
+    matrixArc4Init(&rc4, key, 4);
+    matrixArc4(&rc4, sectorbuf, sectorbuf, length);
+
+    if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) {
+        fprintf(stderr,"[ERR]  Seek failed\n");
+        return -1;
+    }
+
+    if ((n = ipod_write(ipod,sectorbuf,newsize)) < 0) {
+        perror("[ERR]  Write failed\n");
+        return -1;
+    }
+
+    if (n < newsize) {
+        fprintf(stderr,"[ERR]  Short write - requested %d bytes, received %d\n"
+                      ,newsize,n);
+        return -1;
+    }
+    fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n);
+
+    x = ipod->diroffset % ipod->sector_size;
+
+    /* Read directory */
+    if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
+
+    n=ipod_read(ipod, sectorbuf, ipod->sector_size);
+    if (n < 0) { return -1; }
+
+    /* Update checksum */
+    fprintf(stderr,"[INFO] Updating checksum to 0x%08x (was 0x%08x)\n",(unsigned int)chksum,le2int(sectorbuf + x + aupd*40 + 28));
+    int2le(chksum,sectorbuf+x+aupd*40+28);
+
+    /* Write directory */    
+    if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
+    n=ipod_write(ipod, sectorbuf, ipod->sector_size);
+    if (n < 0) { return -1; }
+
+    return 0;
+}
+
+#endif
diff --git a/rbutil/ipodpatcher/ipodpatcher.h b/rbutil/ipodpatcher/ipodpatcher.h
index d816c68..0d92222 100644
--- a/rbutil/ipodpatcher/ipodpatcher.h
+++ b/rbutil/ipodpatcher/ipodpatcher.h
@@ -47,6 +47,8 @@
 int getmodel(struct ipod_t* ipod, int ipod_version);
 int ipod_scan(struct ipod_t* ipod);
 int write_dos_partition_table(struct ipod_t* ipod);
+int read_aupd(struct ipod_t* ipod, char* filename);
+int write_aupd(struct ipod_t* ipod, char* filename);
 off_t filesize(int fd);
 
 #endif
diff --git a/rbutil/ipodpatcher/main.c b/rbutil/ipodpatcher/main.c
index c47063c..f113c8a 100644
--- a/rbutil/ipodpatcher/main.c
+++ b/rbutil/ipodpatcher/main.c
@@ -45,6 +45,8 @@
    ADD_BOOTLOADER,
    READ_FIRMWARE,
    WRITE_FIRMWARE,
+   READ_AUPD,
+   WRITE_AUPD,
    READ_PARTITION,
    WRITE_PARTITION,
    FORMAT_PARTITION,
@@ -89,6 +91,8 @@
     fprintf(stderr,"  -d,   --delete-bootloader\n");
     fprintf(stderr,"  -f,   --format\n");
     fprintf(stderr,"  -c,   --convert\n");
+    fprintf(stderr,"        --read-aupd          filename.bin\n");
+    fprintf(stderr,"        --write-aupd         filename.bin\n");
     fprintf(stderr,"\n");
 
 #ifdef __WIN32__
@@ -299,6 +303,18 @@
                    (strcmp(argv[i],"--format")==0)) {
             action = FORMAT_PARTITION;
             i++;
+        } else if (strcmp(argv[i],"--read-aupd")==0) {
+            action = READ_AUPD;
+            i++;
+            if (i == argc) { print_usage(); return 1; }
+            filename=argv[i];
+            i++;
+        } else if (strcmp(argv[i],"--write-aupd")==0) {
+            action = WRITE_AUPD;
+            i++;
+            if (i == argc) { print_usage(); return 1; }
+            filename=argv[i];
+            i++;
         } else if ((strcmp(argv[i],"-c")==0) || 
                    (strcmp(argv[i],"--convert")==0)) {
             action = CONVERT_TO_FAT32;
@@ -444,6 +460,22 @@
         } else {
             fprintf(stderr,"[ERR]  --read-firmware failed.\n");
         }
+    } else if (action==READ_AUPD) {
+        if (read_aupd(&ipod, filename)==0) {
+            fprintf(stderr,"[INFO] AUPD image read to file %s.\n",filename);
+        } else {
+            fprintf(stderr,"[ERR]  --read-aupd failed.\n");
+        }
+    } else if (action==WRITE_AUPD) {
+        if (ipod_reopen_rw(&ipod) < 0) {
+            return 5;
+        }
+
+        if (write_aupd(&ipod, filename)==0) {
+            fprintf(stderr,"[INFO] AUPD image %s written to device.\n",filename);
+        } else {
+            fprintf(stderr,"[ERR]  --write-aupd failed.\n");
+        }
     } else if (action==READ_PARTITION) {
         outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE);
         if (outfile < 0) {