commit c9ecb5dd6eeb574fc9941f2f1d6694043c0c80c0
Author: Christophe Fergeau <[email protected]>
Date:   Sun Oct 18 20:56:22 2009 +0200

    initial work on reading and storing HashInfo files to the iPod

 src/Makefile.am    |    1 +
 src/itdb_hash72.c  |  292 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/itdb_private.h |   10 ++
 3 files changed, 303 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 76b3c45..d911651 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,6 +9,7 @@ libgpod_la_SOURCES =            \
        itdb_artwork.c          \
        itdb_chapterdata.c      \
        itdb_device.c           \
+       itdb_hash72.c           \
        itdb_iphone.c           \
        itdb_itunesdb.c         \
        itdb_photoalbum.c       \
diff --git a/src/itdb_hash72.c b/src/itdb_hash72.c
new file mode 100644
index 0000000..d9e028d
--- /dev/null
+++ b/src/itdb_hash72.c
@@ -0,0 +1,292 @@
+/*
+| Copyright (c) 2009 Chris Lee <[email protected]>
+| licensed under the terms of the WTFPL <http://sam.zoy.org/wtfpl/>
+*/
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include "rijndael.h"
+
+#include "itdb_device.h"
+#include "db-itunes-parser.h"
+#include "itdb_private.h"
+
+static const uint8_t AES_KEY[16] = { 0x0, };
+
+/* Generate a signature for an iTunesDB or a cbk file using the file SHA1
+ * and a (IV, random bytes) for this device we want to sign for
+ */
+static void hash_generate(uint8_t signature[46],
+                         const uint8_t sha1[20],
+                         const uint8_t iv[16], 
+                         const uint8_t random_bytes[12])
+{
+       uint8_t output[32] = { 0 }, plaintext[32] = { 0 };
+       memcpy(plaintext, sha1, 20);
+       memcpy(&plaintext[20], random_bytes, 12);
+
+       signature[0] = 0x01;
+       signature[1] = 0x00;
+       memcpy(&signature[2], random_bytes, 12);
+
+       aes_set_key((uint8_t *)AES_KEY);
+       aes_encrypt(iv, plaintext, output, 32);
+
+       memcpy(&signature[14], output, 32);
+}
+
+/* Given a valid signature and SHA1 for a file, extracts a valid (IV,
+ * random bytes) couple which can be used to sign any iTunesDB or cbk file
+ * for the device the signature was made for
+ */
+static int hash_extract(const uint8_t signature[46],
+                       const uint8_t sha1[20],
+                       uint8_t iv[16], 
+                       uint8_t random_bytes[12])
+{
+       uint8_t plaintext[32] = { 0 }, output[32] = { 0 };
+
+       if (signature[0] != 0x01 || signature[1] != 0x00) {
+               fprintf(stderr, "Invalid signature prefix!\n");
+               return -1;
+       }
+
+       memcpy(plaintext, sha1, 20);
+       memcpy(&plaintext[20], &signature[2], 12);
+
+       memcpy(output, plaintext, 32);
+
+       aes_set_key((uint8_t *)AES_KEY);
+       aes_decrypt(plaintext, (uint8_t *)&signature[14], output, 16);
+
+       if (memcmp(&plaintext[16], &output[16], 16)) {
+               fprintf(stderr, "uh oh\n");
+               return -1;
+       }
+
+       memcpy(iv, output, 16);
+       memcpy(random_bytes, &signature[2], sizeof(random_bytes));
+
+       return 0;
+}
+
+static int ord_from_hex_char(const char c)
+{
+  if ('0' <= c && c <= '9')
+    return c - '0';
+  else if ('a' <= c && c <= 'f')
+    return 10 + (c - 'a');
+  else if ('A' <= c && c <= 'F')
+    return 10 + (c - 'A');
+  else
+    return -1;
+}
+
+static int string_to_hex(unsigned char *dest, const int array_size,
+                        const char *s)
+{
+  int l;
+
+  /* skip optional '0x' prefix */
+  if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
+    s += 2;
+
+  if ((l = strlen(s)) < array_size*2)
+    return -8;
+
+  do {
+    int low, high;
+    if((high = ord_from_hex_char(s[0])) == -1 ||
+       (low = ord_from_hex_char(s[1])) == -1)
+      return -9;
+    *dest++ = high<<4 | low;
+  } while(*(s+=2));
+  return 0;
+}
+
+static gboolean get_uuid (const Itdb_Device *device, unsigned char uuid[20])
+{
+    char *uuid_str;
+    int result;
+
+    uuid_str = itdb_device_get_uuid (device);
+    result = string_to_hex (uuid, sizeof (uuid), uuid_str);
+
+    return (result == 0);
+}
+
+struct Hash78Info {
+    unsigned char header[6];
+    unsigned char uuid[20];
+    unsigned char rndpart[12];
+    unsigned char iv[16];
+} __attribute__((__packed__));
+
+static char *get_hash_info_path (const Itdb_Device *device)
+{
+    char *filename;
+    char *device_dir;
+
+    device_dir = itdb_get_device_dir (device->mountpoint);
+    filename = g_build_filename (device_dir, "HashInfo", NULL);
+    g_free (device_dir);
+
+    return filename;
+}
+
+static gboolean write_hash_info (const Itdb_Device *device,
+                                unsigned char iv[16],
+                                unsigned char rndpart[12])
+{
+    struct Hash78Info hash_info;
+    char *filename;
+    gboolean success;
+    const char header[] = "HASHv0";
+
+    memcpy (hash_info.header, header, sizeof (header));
+    success = get_uuid (device, hash_info.uuid);
+    if (!success) {
+       return FALSE;
+    }
+    memcpy (hash_info.rndpart, rndpart, sizeof (hash_info.rndpart));
+    memcpy (hash_info.iv, iv, sizeof (hash_info.iv));
+
+    filename = get_hash_info_path (device);
+    success = g_file_set_contents (filename, (void *)&hash_info,
+                                  sizeof (hash_info), NULL);
+    g_free (filename);
+    
+    return success;
+}
+
+static struct Hash78Info *read_hash_info (const Itdb_Device *device)
+{
+    char *filename;
+    gsize len;
+    gboolean read_ok;
+    unsigned char uuid[20];
+    struct Hash78Info *info;
+
+    if (!get_uuid (device, uuid)) {
+       return NULL;
+    }
+
+    filename = get_hash_info_path (device);
+
+    read_ok = g_file_get_contents (filename, (gchar**)&info, &len, NULL);
+    g_free (filename);
+    if (!read_ok) {
+       return NULL;
+    }
+    if (len != sizeof (*info)) {
+       g_free (info);
+       return NULL;
+    }
+    if (memcmp (info->uuid, uuid, sizeof (uuid)) != 0) {
+       g_free (info);
+       return NULL;
+    }
+
+    return info;
+}
+
+static void itdb_hash72_compute_itunesdb_sha1 (unsigned char *itdb_data, 
+                                              gsize itdb_len,
+                                              unsigned char sha1[20])
+{
+    guchar backup18[8];
+    guchar backup32[20];
+    guchar hash72[46];
+    gsize sha1_len;
+    GChecksum *checksum;
+    MhbdHeader *header;
+
+    g_assert (itdb_len >= 0x6c);
+
+    header = (MhbdHeader *)itdb_data;
+    g_assert (strncmp (header->header_id, "mhbd", strlen ("mhbd")) == 0);
+    memcpy (backup18, &header->db_id, sizeof (backup18));
+    memcpy (backup32, &header->unknown6, sizeof (backup32));
+    memcpy (hash72, &header->hash72, sizeof (hash72));
+
+    /* Those fields must be zero'ed out for the sha1 calculation */
+    memset(&header->db_id, 0, sizeof (header->db_id));
+
+    memset(&header->hash58, 0, sizeof (header->hash58));
+    memset(&header->hash72, 0, sizeof (header->hash72));
+
+    sha1_len = sizeof (sha1);
+    checksum = g_checksum_new (G_CHECKSUM_SHA1);
+    g_checksum_update (checksum, itdb_data, itdb_len);
+    g_checksum_get_digest (checksum, sha1, &sha1_len);
+    g_checksum_free (checksum);
+
+    memcpy (&header->db_id, backup18, sizeof (backup18));
+    memcpy (&header->unknown6, backup32, sizeof (backup32));
+}
+
+gboolean itdb_hash72_extract_hash_info (const Itdb_Device *device, 
+                                       unsigned char *itdb_data, 
+                                       gsize itdb_len)
+{
+    guchar hash72[46];
+    guchar sha1[20];
+    guchar iv[16];
+    guchar random_bytes[12];
+    MhbdHeader *header;
+    int iv_extracted;
+
+    if (itdb_len < 0x6c) {
+       return FALSE;
+    }
+
+    header = (MhbdHeader *)itdb_data;
+    g_assert (strncmp (header->header_id, "mhbd", strlen ("mhbd")) == 0);
+    memcpy (hash72, &header->hash72, sizeof (hash72));
+
+    itdb_hash72_compute_itunesdb_sha1 (itdb_data, itdb_len, sha1);
+
+    iv_extracted = hash_extract (hash72, sha1, iv, random_bytes);
+    if (iv_extracted != 0) {
+       return FALSE;
+    }
+
+    return write_hash_info (device, iv, random_bytes);
+}
+
+gboolean itdb_hash72_compute_hash_for_sha1 (const Itdb_Device *device, 
+                                           const guchar sha1[20],
+                                           guchar signature[46])
+{
+    struct Hash78Info *hash_info;
+
+    hash_info = read_hash_info (device);
+    if (hash_info == NULL) {
+       return FALSE;
+    }
+    hash_generate (signature, sha1, hash_info->iv, hash_info->rndpart);
+    g_free (hash_info);
+
+    return TRUE;
+}
+
+gboolean itdb_hash72_write_hash (const Itdb_Device *device, 
+                                unsigned char *itdb_data, 
+                                gsize itdb_len,
+                                GError **error)
+{
+    guchar sha1[20];
+    MhbdHeader *header;
+
+    if (itdb_len < 0x6c) {
+       g_set_error (error, 0, -1, "iTunesDB file too small to write checksum");
+       return FALSE;
+    }
+
+    itdb_hash72_compute_itunesdb_sha1 (itdb_data, itdb_len, sha1);
+    header = (MhbdHeader *)itdb_data;
+    return itdb_hash72_compute_hash_for_sha1 (device, sha1, header->hash72);
+}
diff --git a/src/itdb_private.h b/src/itdb_private.h
index bef9be5..eab8d24 100644
--- a/src/itdb_private.h
+++ b/src/itdb_private.h
@@ -203,6 +203,16 @@ G_GNUC_INTERNAL gboolean itdb_file_set_contents (const 
char *filename,
                                                  const char *data, gssize len, 
                                                  GError **error);
 G_GNUC_INTERNAL int itdb_sqlite_generate_itdbs(FExport *fexp);
+G_GNUC_INTERNAL gboolean itdb_hash72_extract_hash_info(const Itdb_Device 
*device, 
+                                                      unsigned char 
*itdb_data, 
+                                                      gsize itdb_len);
+G_GNUC_INTERNAL gboolean itdb_hash72_write_hash (const Itdb_Device *device, 
+                                                unsigned char *itdb_data, 
+                                                gsize itdb_len,
+                                                GError **error);
+G_GNUC_INTERNAL gboolean itdb_hash72_compute_hash_for_sha1 (const Itdb_Device 
*device, 
+                                                           const guchar 
sha1[20],
+                                                           guchar 
signature[46]);
 #ifdef HAVE_LIBIPHONE
 G_GNUC_INTERNAL int itdb_iphone_start_sync(Itdb_Device *device, void 
**prepdata);
 G_GNUC_INTERNAL int itdb_iphone_stop_sync(void *sync_ctx);

------------------------------------------------------------------------------
Return on Information:
Google Enterprise Search pays you back
Get the facts.
http://p.sf.net/sfu/google-dev2dev
_______________________________________________
gtkpod-cvs2 mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/gtkpod-cvs2

Reply via email to