[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]
Re: 2nd Patch - BIFF5 era XOR crypto
- From: Nick Lamb <njl98r ecs soton ac uk>
- To: gnumeric-list gnome org
- Subject: Re: 2nd Patch - BIFF5 era XOR crypto
- Date: Sat, 17 Jan 2004 02:46:06 +0000
On Fri, Jan 16, 2004 at 10:27:18AM -0500, Jody Goldberg wrote:
> Please send the patch.
Oops. Attached patch is cumulative (includes the parts you've already
applied to 1.3) let me know if that's too hard to fix, I think you can
just throw away all except the ms-excel-read.c stuff
Nick.
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 16 Jan 2004 14:12:30 -0000
@@ -49,13 +49,74 @@
/* 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 (hash != pw_hash)
+ return FALSE;
+
+ ms_biff_crypt_seq(q->xor_key, key, password);
+
+ q->encryption = MS_BIFF_CRYPTO_XOR;
+ return TRUE;
}
/*
@@ -230,7 +291,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 +300,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 +327,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 +396,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 +427,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 16 Jan 2004 14:12:30 -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;
Index: ms-excel-read.c
===================================================================
RCS file: /cvs/gnome/gnumeric/plugins/excel/ms-excel-read.c,v
retrieving revision 1.564.2.1
diff -u -r1.564.2.1 ms-excel-read.c
--- ms-excel-read.c 6 Jan 2004 03:34:49 -0000 1.564.2.1
+++ ms-excel-read.c 16 Jan 2004 14:12:32 -0000
@@ -4761,6 +4761,32 @@
g_ptr_array_add (container->v7.externsheet, sheet);
}
+/* FILEPASS, ask the user for a password if necessary
+ * return value is an error string, or NULL for success
+ */
+
+static char *
+excel_read_FILEPASS (BiffQuery *q, ExcelWorkbook *ewb)
+{
+ /* files with workbook protection are encrypted using a
+ * static password (why ?? ).
+ */
+ if (ms_biff_query_set_decrypt(q, ewb->container.ver, "VelvetSweatshop"))
+ return NULL;
+
+ do {
+ char const *filename = workbook_get_filename (ewb->gnum_wb);
+ char *passwd = gnm_cmd_context_get_password (GNM_CMD_CONTEXT (ewb->context), filename);
+ if (passwd == NULL) {
+ return _("No password supplied");
+ }
+ if (ms_biff_query_set_decrypt (q, ewb->container.ver, passwd)) {
+ return NULL;
+ }
+ g_free (passwd);
+ } while (TRUE);
+}
+
static gboolean
excel_read_sheet (BiffQuery *q, ExcelWorkbook *ewb,
WorkbookView *wb_view, ExcelReadSheet *esheet)
@@ -5112,6 +5138,15 @@
case BIFF_FORMAT: excel_read_FORMAT (q, ewb); break;
case BIFF_STYLE: break;
case BIFF_1904: excel_read_1904 (q, ewb); break;
+ case BIFF_FILEPASS:
+ {
+ char *problem = excel_read_FILEPASS (q, ewb);
+ if (problem) {
+ gnm_cmd_context_error_import (GNM_CMD_CONTEXT (ewb->context), problem);
+ return FALSE;
+ }
+ }
+ break;
default:
excel_unexpected_biff (q, "Sheet", ms_excel_read_debug);
@@ -5495,25 +5530,7 @@
break;
case BIFF_FILEPASS: /* All records after this are encrypted */
- /* files with workbook protection are encrypted using a
- * static password (why ?? ).
- */
- if (ms_biff_query_set_decrypt (q, ewb->container.ver, "VelvetSweatshop"))
- break;
- do {
- char const *filename = workbook_get_filename (ewb->gnum_wb);
- char *passwd = gnm_cmd_context_get_password (GNM_CMD_CONTEXT (ewb->context), filename);
- if (passwd == NULL) {
- problem_loading = _("No password supplied");
- break;
- }
- if (!ms_biff_query_set_decrypt (q, ewb->container.ver, passwd))
- problem_loading = _("Invalid password");
- g_free (passwd);
- if (problem_loading == NULL)
- break;
- problem_loading = NULL;
- } while (TRUE);
+ problem_loading= excel_read_FILEPASS(q, ewb);
break;
case BIFF_STYLE:
[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]