doy / spreadsheet-parsexlsx

parse XLSX files
http://metacpan.org/release/Spreadsheet-ParseXLSX
27 stars 35 forks source link

Cannot open password protected SHA1 encrypted files. #68

Closed feinorgh closed 6 months ago

feinorgh commented 8 years ago

I've been trying to get this module to work with XLSX files produced by Excel 14.0.7166.5000 (32-bit) (from the Microsoft Office Professional Plus 2010 package).

I tried to see what happened through the module, and realized the first problem is that it interprets the hashing algorithm as SHA1 (not SHA-1).

diff --git a/lib/Spreadsheet/ParseXLSX/Decryptor.pm b/lib/Spreadsheet/ParseXLSX/
Decryptor.pm
index 4968a7d..3a84e15 100644
--- a/lib/Spreadsheet/ParseXLSX/Decryptor.pm
+++ b/lib/Spreadsheet/ParseXLSX/Decryptor.pm
@@ -93,6 +93,8 @@ sub _standardDecryption {

     my ($cipherAlgorithm, $hashAlgorithm);

+    print "\$algID: $algID, \$algIDHash: $algIDHash\n";
+
     if ($algID == 0x0000660E || $algID == 0x0000660F || $algID == 0x0000660E) {
         $cipherAlgorithm = 'AES';
     } else {
@@ -194,7 +196,7 @@ sub new {

     if ($self->{hashAlgorithm} eq 'SHA512') {
         $self->{hashProc} = \&Digest::SHA::sha512;
-    } elsif ($self->{hashAlgorithm} eq 'SHA-1') {
+    } elsif ($self->{hashAlgorithm} eq 'SHA-1' || $self->{hashAlgorithm} eq 'SHA1') {
         $self->{hashProc} = \&Digest::SHA::sha1;
     } elsif ($self->{hashAlgorithm} eq 'SHA256') {
         $self->{hashProc} = \&Digest::SHA::sha256;

So after this, I got a new error, namely that the wrong password was given. So I compared the produced hashes and realized they are similar, but possibly with the wrong bit size, since the hash is zero padded when unpacked:

diff --git a/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm b/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm
index 0405a68..902c368 100644
--- a/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm
+++ b/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm
@@ -94,6 +94,11 @@ sub verifyPassword {
     my $encryptedVerifierHash0 = $self->{hashProc}->($self->decrypt($encryptedVerifier, "\xfe\xa7\xd2\x76\x3b\x4b\x9e\x79"));
     $encryptedVerifierHash = $self->decrypt($encryptedVerifierHash, "\xd7\xaa\x0f\x6d\x30\x61\x34\x4e");

+    use Data::Dump;
+    dd($encryptedVerifierHash0);
+    dd($encryptedVerifierHash);
+    print "'$encryptedVerifierHash0'\n";
+    print "'$encryptedVerifierHash'\n";
     die "Wrong password: $self" unless ($encryptedVerifierHash0 eq $encryptedVerifierHash);
 }

pack("H*","053129c83b159a03a74b824a5cfe1ed4690a52fe")
pack("H*","053129c83b159a03a74b824a5cfe1ed4690a52fe000000000000000000000000")
'1)▒;▒▒K▒J\▒▒i
R▒'
'1)▒;▒▒K▒J\▒▒i
R▒'

So they "look" similar produced as strings but do not compare to the same value (I suppose). I could not dig deeper into the issue currently, unfortunately, but there might be an easy fix. My knowledge of hashes and encryption routines is however limited, but it might be obvious to someone else.

Attached is the example Excel file protected by the password "foobar".

sha1-password-protected.xlsx

Tux commented 7 years ago

I also hit this problem. Could you turn that into a PR please so that doy can release?

feinorgh commented 7 years ago

There's an active pull request already: https://github.com/doy/spreadsheet-parsexlsx/pull/69