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