Index: ms-biff.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/excel/ms-biff.c,v retrieving revision 1.64 diff -u -r1.64 ms-biff.c --- ms-biff.c 25 Nov 2003 06:04:08 -0000 1.64 +++ ms-biff.c 9 Dec 2003 01:37:35 -0000 @@ -49,13 +49,78 @@ /* Read Side */ /*******************************************************************************/ +/** + * ms_biff_password_hash and ms_biff_crypt_seq + * based on pseudo-code in the OpenOffice.org XL documentation + **/ + +static guint16 +ms_biff_password_hash (char const *password) +{ + int tmp, index= 0, len= strlen(password); + guint16 chr, hash= 0; + + do { + chr = password[index]; + index++; + tmp = (chr << index); + hash ^= (tmp & 0x7fff) | (tmp >> 15); + } while (index < len); + hash = hash ^ len ^ 0xce4b; + + return hash; +} + +static void +ms_biff_crypt_seq (guint8 *seq, guint16 key, char const *password) +{ + guint8 low = key & 0xff, high = key >> 8; + guint8 preset[15] = {0xbb, 0xff, 0xff, 0xba, 0xff, 0xff, 0xb9, 0x80, + 0x00, 0xbe, 0x0f, 0x00, 0xbf, 0x0f, 0x00 }; + int k, len= strlen(password); + + strcpy(seq, password); + for (k= 0; len + k < 16; ++k) { + seq[len + k]= preset[k]; + } + for (k= 0; k < 16; k+=2) { + seq[k] ^= low; + seq[k+1] ^= high; + } + for (k= 0; k < 16; ++k) { + seq[k] = (seq[k] << 2) | (seq[k] >> 6); + } +} + static gboolean ms_biff_pre_biff8_query_set_decrypt (BiffQuery *q, char const *password) { - g_return_val_if_fail (q->length == sizeof_BIFF_2_7_FILEPASS, FALSE); + guint16 hash, key; + guint16 pw_hash = ms_biff_password_hash(password); + -#warning TODO OO has docs should be trivial to add - return FALSE; + if (q->length == 4) { + key = GSF_LE_GET_GUINT16(q->data + 0); + hash = GSF_LE_GET_GUINT16(q->data + 2); + } else if (q->length == 6) { + /* BIFF8 record with pre-biff8 crypto, these do exist */ + key = GSF_LE_GET_GUINT16(q->data + 2); + hash = GSF_LE_GET_GUINT16(q->data + 4); + } else { + return FALSE; + } + +#if BIFF_DEBUG > 0 + printf("Stored hash = %04X password hash = %04X\n", hash, pw_hash); +#endif + + if (hash != pw_hash) + return FALSE; + + ms_biff_crypt_seq(q->xor_key, key, password); + + q->encryption = MS_BIFF_CRYPTO_XOR; + return TRUE; } /* @@ -230,7 +295,7 @@ if (password == NULL) return FALSE; - if (version < MS_BIFF_V8) + if (version < MS_BIFF_V8 || q->data[0] == 0) return ms_biff_pre_biff8_query_set_decrypt (q, password); g_return_val_if_fail (q->length == sizeof_BIFF_8_FILEPASS, FALSE); @@ -239,7 +304,7 @@ q->data + 22, q->data + 38, &q->md5_ctxt)) return FALSE; - q->is_encrypted = TRUE; + q->encryption = MS_BIFF_CRYPTO_RC4; q->block = -1; /* For some reaons the 1st record after FILEPASS seems to be unencrypted */ @@ -266,7 +331,7 @@ q->data_malloced = q->non_decrypted_data_malloced = FALSE; q->data = q->non_decrypted_data = NULL; q->input = input; - q->is_encrypted = FALSE; + q->encryption = MS_BIFF_CRYPTO_NONE; #if BIFF_DEBUG > 0 ms_biff_query_dump (q); @@ -335,7 +400,7 @@ } else q->data = NULL; - if (q->is_encrypted) { + if (q->encryption == MS_BIFF_CRYPTO_RC4) { q->non_decrypted_data_malloced = q->data_malloced; q->non_decrypted_data = q->data; @@ -366,6 +431,21 @@ rc4 (data, len, &q->rc4_key); } + } else if (q->encryption == MS_BIFF_CRYPTO_XOR) { + unsigned int offset, k; + + q->non_decrypted_data_malloced = q->data_malloced; + q->non_decrypted_data = q->data; + q->data_malloced = TRUE; + q->data = g_new (guint8, q->length); + memcpy (q->data, q->non_decrypted_data, q->length); + + offset = (q->streamPos + q->length + 4) % 16; + for (k= 0; k < q->length; ++k) { + guint8 tmp = (q->data[k] << 3) | (q->data[k] >> 5); + q->data[k] = tmp ^ q->xor_key[offset]; + offset = (offset + 1) % 16; + } } else q->non_decrypted_data = q->data; Index: ms-biff.h =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/excel/ms-biff.h,v retrieving revision 1.55 diff -u -r1.55 ms-biff.h --- ms-biff.h 25 Nov 2003 06:04:08 -0000 1.55 +++ ms-biff.h 9 Dec 2003 01:37:35 -0000 @@ -13,6 +13,11 @@ #include "rc4.h" #include "md5.h" +typedef enum { MS_BIFF_CRYPTO_NONE = 0, + MS_BIFF_CRYPTO_XOR, + MS_BIFF_CRYPTO_RC4, + MS_BIFF_CRYPTO_UNKNOWN } MsBiffCrypto ; + typedef enum { MS_BIFF_V2 = 2, MS_BIFF_V3 = 3, MS_BIFF_V4 = 4, @@ -41,7 +46,8 @@ guint32 streamPos; GsfInput *input; - gboolean is_encrypted; + MsBiffCrypto encryption; + guint8 xor_key[16]; RC4_KEY rc4_key; MD5_CTX md5_ctxt; int block;