[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: 2nd Patch - BIFF5 era XOR crypto



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]