From: Christophe F. <te...@us...> - 2011-01-01 14:07:38
|
commit b9b83dc8b6c3d1f0c53ed32f05279ca838d54e02 Author: Nikias Bassen <ni...@gm...> Date: Mon Oct 4 01:15:39 2010 +0200 add hashAB support via external module configure.ac | 10 +++ src/Makefile.am | 2 + src/db-itunes-parser.h | 2 + src/itdb_device.c | 1 + src/itdb_hashAB.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++ src/itdb_itunesdb.c | 30 +++++++--- src/itdb_private.h | 24 +++++--- src/itdb_sqlite.c | 54 +++++++++++++----- 8 files changed, 237 insertions(+), 31 deletions(-) --- diff --git a/configure.ac b/configure.ac index 8704506..296f56e 100644 --- a/configure.ac +++ b/configure.ac @@ -192,6 +192,16 @@ AC_SUBST(TMPMOUNTDIR) AH_TEMPLATE([TMPMOUNTDIR], [Directory where HAL/udev will create a sub-directory to mount iPods]) AC_DEFINE_UNQUOTED(TMPMOUNTDIR, "$with_temp_mount_dir", [Directory where HAL/udev will create a sub-directory to mount iPods]) +dnl *********************************************************************** +dnl * provide a $l{ibdir}/libgpod directory for external modules +dnl *********************************************************************** +AC_ARG_WITH(libgpod-blob-dir, [AC_HELP_STRING([--with-libgpod-blob-dir=PATH], + [Load libgpod binary blobs from this directory [LIBDIR/libgpod]])], + [], + [with_libgpod_blob_dir='${libdir}/libgpod']) +LIBGPOD_BLOB_DIR=$with_libgpod_blob_dir +AC_SUBST(LIBGPOD_BLOB_DIR) + dnl ************************************************** dnl * TagLib is only used by test-rebuild-db dnl ************************************************** diff --git a/src/Makefile.am b/src/Makefile.am index ce02831..90148f0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,6 +11,7 @@ libgpod_la_SOURCES = \ itdb_device.c \ itdb_hash58.c \ itdb_hash72.c \ + itdb_hashAB.c \ itdb_iphone.c \ itdb_itunesdb.c \ itdb_photoalbum.c \ @@ -32,6 +33,7 @@ libgpod_la_CFLAGS = -DWITH_INTERNAL_GCHECKSUM endif libgpod_la_LDFLAGS = -version-info $(LIBGPOD_SO_VERSION) -no-undefined +libgpod_la_CPPFLAGS = -DLIBGPOD_BLOB_DIR=\"$(LIBGPOD_BLOB_DIR)\" libgpodincludebase = $(includedir)/gpod-1.0 libgpodincludedir = $(libgpodincludebase)/gpod diff --git a/src/db-itunes-parser.h b/src/db-itunes-parser.h index cc71bcc..80d5213 100644 --- a/src/db-itunes-parser.h +++ b/src/db-itunes-parser.h @@ -102,6 +102,8 @@ struct _MhbdHeader { guint16 unk_0xa4; guint16 unk_0xa6; guint16 unk_0xa8; + guchar align_0xa9; + guchar hashAB[57]; guchar padding[]; } __attribute__((__packed__)); diff --git a/src/itdb_device.c b/src/itdb_device.c index 42c4b23..f1aabad 100644 --- a/src/itdb_device.c +++ b/src/itdb_device.c @@ -1938,6 +1938,7 @@ G_GNUC_INTERNAL gboolean itdb_device_write_checksum (Itdb_Device *device, case ITDB_CHECKSUM_HASH72: return itdb_hash72_write_hash (device, itdb_data, itdb_len, error); case ITDB_CHECKSUM_HASHAB: + return itdb_hashAB_write_hash (device, itdb_data, itdb_len, error); case ITDB_CHECKSUM_UNKNOWN: g_set_error (error, 0, -1, "Unsupported checksum type"); return FALSE; diff --git a/src/itdb_hashAB.c b/src/itdb_hashAB.c new file mode 100644 index 0000000..ce0f794 --- /dev/null +++ b/src/itdb_hashAB.c @@ -0,0 +1,145 @@ +/* +| Copyright (c) 2010 Nikias Bassen <ni...@gm...> +| +| The code contained in this file is free software; you can redistribute +| it and/or modify it under the terms of the GNU Lesser General Public +| License as published by the Free Software Foundation; either version +| 2.1 of the License, or (at your option) any later version. +| +| This file 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 +| Lesser General Public License for more details. +| +| You should have received a copy of the GNU Lesser General Public +| License along with this code; if not, write to the Free Software +| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +| USA +| +| iTunes and iPod are trademarks of Apple +| +| This product is not supported/written/published by Apple! +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include "rijndael.h" + +#include <glib/gstdio.h> + +#include <gmodule.h> + +#include "itdb_device.h" +#include "db-itunes-parser.h" +#include "itdb_private.h" + +typedef void (*calcHashAB_t)(unsigned char target[57], const unsigned char sha1[20], const unsigned char uuid[20], const unsigned char rnd_bytes[23]); +static calcHashAB_t calc_hashAB = NULL; + +static gboolean load_libhashab() +{ + gchar *path; + GModule *handle; + + if (!g_module_supported()) { + return FALSE; + } + path = g_module_build_path(LIBGPOD_BLOB_DIR, "hashab"); + handle = g_module_open(path, G_MODULE_BIND_LAZY); + g_free(path); + if (!handle) { + return FALSE; + } + + if (!g_module_symbol(handle, "calcHashAB", (void**)&calc_hashAB)) { + g_module_close(handle); + g_warning("symbol 'calcHashAB' not found"); + return FALSE; + } + g_module_make_resident(handle); + + printf("***** hashAB support successfully loaded *****\n"); + + return TRUE; +} + +static void itdb_hashAB_compute_itunesdb_sha1 (unsigned char *itdb_data, + gsize itdb_len, + unsigned char sha1[20]) +{ + guchar backup18[8]; + guchar backup32[20]; + guchar hashAB[57]; + 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->unk_0x32, sizeof (backup32)); + memcpy (hashAB, &header->hashAB, sizeof (hashAB)); + + /* 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)); + memset(&header->hashAB, 0, sizeof (header->hashAB)); + + sha1_len = g_checksum_type_get_length (G_CHECKSUM_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->unk_0x32, backup32, sizeof (backup32)); +} + +gboolean itdb_hashAB_compute_hash_for_sha1 (const Itdb_Device *device, + const guchar sha1[20], + guchar signature[57], + GError **error) +{ + unsigned char uuid[20]; + unsigned char rndpart[23] = "ABCDEFGHIJKLMNOPQRSTUVW"; + + if (calc_hashAB == NULL) { + if (!load_libhashab()) { + g_set_error (error, 0, -1, "Unsupported checksum type"); + return FALSE; + } + } + + if (!get_uuid(device, uuid)) return FALSE; + + calc_hashAB(signature, sha1, uuid, rndpart); + + return TRUE; +} + +gboolean itdb_hashAB_write_hash (const Itdb_Device *device, + unsigned char *itdb_data, + gsize itdb_len, + GError **error) +{ + guchar sha1[20]; + MhbdHeader *header; + + if (itdb_len < 0xF4) { + g_set_error (error, 0, -1, "iTunesDB file too small to write checksum"); + return FALSE; + } + + header = (MhbdHeader *)itdb_data; + header->hashing_scheme = GUINT16_FROM_LE (ITDB_CHECKSUM_HASHAB); + itdb_hashAB_compute_itunesdb_sha1 (itdb_data, itdb_len, sha1); + return itdb_hashAB_compute_hash_for_sha1 (device, sha1, header->hashAB, error); +} diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c index 7f587bf..9ccf61a 100644 --- a/src/itdb_itunesdb.c +++ b/src/itdb_itunesdb.c @@ -3801,7 +3801,7 @@ static void mk_mhbd (FExport *fexp, guint32 children) cts = fexp->wcontents; put_header (cts, "mhbd"); - put32lint (cts, 188); /* header size */ + put32lint (cts, 244); /* header size */ put32lint (cts, -1); /* size of whole mhdb -- fill in later */ if (itdb_device_supports_compressed_itunesdb (fexp->itdb->device)) { /* 2 on iPhone 3.0 and Nano 5G, 1 on iPod Color and iPod Classic @@ -3830,10 +3830,12 @@ static void mk_mhbd (FExport *fexp, guint32 children) 0x19 = iTunes 7.4 0x28 = iTunes 8.2.1 0x2a = iTunes 9.0.1 + 0x2e = iTunes 9.1 + 0x30 = iTunes 9.2 Be aware that newer ipods won't work if the library version number is too old */ - fexp->itdb->version = 0x2a; + fexp->itdb->version = 0x30; put32lint (cts, fexp->itdb->version); put32lint (cts, children); put64lint (cts, fexp->itdb->id); @@ -3857,20 +3859,32 @@ static void mk_mhbd (FExport *fexp, guint32 children) /* seen: 0x01 for iPod Color */ put32lint (cts, fexp->itdb->priv->unk_0x54); /* unknown: seen: 0x4d for nano 3G */ /* seen: 0x0f for iPod Color */ - put32_n0 (cts, 5); /* 20 bytes hash */ + put32_n0 (cts, 5); /* 20 bytes hash58 */ put32lint (cts, fexp->itdb->tzoffset); /* timezone offset in seconds */ /* 0x70 */ - put16lint (cts, 2); /* without it, iTunes thinks iPhone databases - are corrupted, 0 on iPod Color */ - put16lint (cts, 0); - put32_n0 (cts, 11); /* new hash */ + switch (itdb_device_get_checksum_type(fexp->itdb->device)) { + case ITDB_CHECKSUM_HASHAB: + put16lint (cts, 4); /* new on 4th gen iOS devices */ + break; + case ITDB_CHECKSUM_HASH72: + put16lint (cts, 2); + break; + default: + put16lint (cts, 0); + break; + } + put16lint (cts, 0); /* hash72 */ + put32_n0 (cts, 11); /* hash72 */ /* 0xa0 */ put16lint (cts, fexp->itdb->priv->audio_language); /* audio_language */ put16lint (cts, fexp->itdb->priv->subtitle_language); /* subtitle_language */ put16lint (cts, fexp->itdb->priv->unk_0xa4); /* unknown */ put16lint (cts, fexp->itdb->priv->unk_0xa6); /* unknown */ put16lint (cts, fexp->itdb->priv->unk_0xa8); /* unknown */ - put16lint (cts, 0); + put8int (cts, 0); + /* 0xab */ + put8int (cts, 0); /* hashAB */ + put32_n0 (cts, 14); /* hashAB */ put32_n0 (cts, 4); /* dummy space */ } diff --git a/src/itdb_private.h b/src/itdb_private.h index afc694e..56dd5d7 100644 --- a/src/itdb_private.h +++ b/src/itdb_private.h @@ -224,24 +224,32 @@ G_GNUC_INTERNAL gint itdb_get_max_photo_id ( Itdb_PhotoDB *db ); G_GNUC_INTERNAL Itdb_iTunesDB *db_get_itunesdb (Itdb_DB *db); G_GNUC_INTERNAL Itdb_PhotoDB *db_get_photodb (Itdb_DB *db); G_GNUC_INTERNAL gint itdb_thumb_get_byteorder (ItdbThumbFormat format); -G_GNUC_INTERNAL time_t device_time_mac_to_time_t (Itdb_Device *device, +G_GNUC_INTERNAL time_t device_time_mac_to_time_t (Itdb_Device *device, guint64 mactime); G_GNUC_INTERNAL guint64 device_time_time_t_to_mac (Itdb_Device *device, time_t timet); G_GNUC_INTERNAL gint itdb_musicdirs_number_by_mountpoint (const gchar *mountpoint); 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, +G_GNUC_INTERNAL gboolean itdb_hashAB_write_hash (const Itdb_Device *device, + unsigned char *itdb_data, + gsize itdb_len, + GError **error); +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, +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_hash58_write_hash (Itdb_Device *device, - unsigned char *itdb_data, +G_GNUC_INTERNAL gboolean itdb_hash58_write_hash (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, +G_GNUC_INTERNAL gboolean itdb_hashAB_compute_hash_for_sha1 (const Itdb_Device *device, + const guchar sha1[20], + guchar signature[57], + GError **error); +G_GNUC_INTERNAL gboolean itdb_hash72_compute_hash_for_sha1 (const Itdb_Device *device, const guchar sha1[20], guchar signature[46], GError **error); diff --git a/src/itdb_sqlite.c b/src/itdb_sqlite.c index a5a0738..4e0197a 100644 --- a/src/itdb_sqlite.c +++ b/src/itdb_sqlite.c @@ -2041,24 +2041,22 @@ error: return FALSE; } -static const guint CBK_HEADER_SIZE = 46; - -static void cbk_calc_sha1_of_sha1s(GArray *cbk) +static void cbk_calc_sha1_of_sha1s(GArray *cbk, guint cbk_header_size) { GChecksum *checksum; unsigned char* final_sha1; unsigned char* sha1s; gsize final_sha1_len; - g_assert (cbk->len > CBK_HEADER_SIZE + 20); + g_assert (cbk->len > cbk_header_size + 20); - final_sha1 = &g_array_index(cbk, guchar, CBK_HEADER_SIZE); - sha1s = &g_array_index(cbk, guchar, CBK_HEADER_SIZE + 20); + final_sha1 = &g_array_index(cbk, guchar, cbk_header_size); + sha1s = &g_array_index(cbk, guchar, cbk_header_size + 20); final_sha1_len = g_checksum_type_get_length(G_CHECKSUM_SHA1); g_assert (final_sha1_len == 20); checksum = g_checksum_new(G_CHECKSUM_SHA1); - g_checksum_update(checksum, sha1s, cbk->len - (CBK_HEADER_SIZE + 20)); + g_checksum_update(checksum, sha1s, cbk->len - (cbk_header_size + 20)); g_checksum_get_digest(checksum, final_sha1, &final_sha1_len); g_checksum_free(checksum); } @@ -2069,12 +2067,30 @@ static gboolean mk_Locations_cbk(Itdb_iTunesDB *itdb, const char *dirname) char *cbk_filename; GArray *cbk; gboolean success; - guchar *cbk_hash72; + guchar *cbk_hash; guchar *final_sha1; + guint cbk_header_size = 0; + guint checksum_type; + + checksum_type = itdb_device_get_checksum_type(itdb->device); + switch (checksum_type) { + case ITDB_CHECKSUM_HASHAB: + cbk_header_size = 57; + break; + case ITDB_CHECKSUM_HASH72: + cbk_header_size = 46; + break; + default: + break; + } + if (cbk_header_size == 0) { + fprintf(stderr, "ERROR: Unsupported checksum type '%d' in cbk file generation!\n", checksum_type); + return FALSE; + } cbk = g_array_sized_new(FALSE, TRUE, 1, - CBK_HEADER_SIZE + 20); - g_array_set_size(cbk, CBK_HEADER_SIZE + 20); + cbk_header_size + 20); + g_array_set_size(cbk, cbk_header_size + 20); locations_filename = g_build_filename(dirname, "Locations.itdb", NULL); success = cbk_calc_sha1s(locations_filename, cbk); @@ -2083,11 +2099,19 @@ static gboolean mk_Locations_cbk(Itdb_iTunesDB *itdb, const char *dirname) g_array_free(cbk, TRUE); return FALSE; } - cbk_calc_sha1_of_sha1s(cbk); - final_sha1 = &g_array_index(cbk, guchar, CBK_HEADER_SIZE); - cbk_hash72 = &g_array_index(cbk, guchar, 0); - success = itdb_hash72_compute_hash_for_sha1 (itdb->device, final_sha1, - cbk_hash72, NULL); + cbk_calc_sha1_of_sha1s(cbk, cbk_header_size); + final_sha1 = &g_array_index(cbk, guchar, cbk_header_size); + cbk_hash = &g_array_index(cbk, guchar, 0); + switch (checksum_type) { + case ITDB_CHECKSUM_HASHAB: + success = itdb_hashAB_compute_hash_for_sha1 (itdb->device, final_sha1, cbk_hash, NULL); + break; + case ITDB_CHECKSUM_HASH72: + success = itdb_hash72_compute_hash_for_sha1 (itdb->device, final_sha1, cbk_hash, NULL); + break; + default: + break; + } if (!success) { g_array_free(cbk, TRUE); return FALSE; |