summaryrefslogtreecommitdiffstats
path: root/lib/Spreadsheet/ParseXLSX/Decryptor/Standard.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Spreadsheet/ParseXLSX/Decryptor/Standard.pm')
-rw-r--r--lib/Spreadsheet/ParseXLSX/Decryptor/Standard.pm87
1 files changed, 87 insertions, 0 deletions
diff --git a/lib/Spreadsheet/ParseXLSX/Decryptor/Standard.pm b/lib/Spreadsheet/ParseXLSX/Decryptor/Standard.pm
new file mode 100644
index 0000000..a940719
--- /dev/null
+++ b/lib/Spreadsheet/ParseXLSX/Decryptor/Standard.pm
@@ -0,0 +1,87 @@
+package Spreadsheet::ParseXLSX::Decryptor::Standard;
+use strict;
+use warnings;
+
+use base 'Spreadsheet::ParseXLSX::Decryptor';
+
+sub new {
+ my $class = shift;
+ my $self = Spreadsheet::ParseXLSX::Decryptor->new(@_);
+ bless $self, $class;
+}
+
+sub decrypt {
+ my $self = shift;
+ my ($encryptedValue) = @_;
+
+ my $key = $self->_generateDecryptionKey("\x00" x 4);
+ my $ecb = Crypt::Mode::ECB->new($self->{cipherAlgorithm}, 0);
+ return $ecb->decrypt($encryptedValue, $key);
+}
+
+sub decryptFile {
+ my $self = shift;
+ my ($inFile, $outFile, $bufferLength, $fileSize) = @_;
+
+ my $key = $self->_generateDecryptionKey("\x00" x 4);
+ my $ecb = Crypt::Mode::ECB->new($self->{cipherAlgorithm}, 0);
+
+ my $inbuf;
+ my $i = 0;
+
+ while (($fileSize > 0) && (my $inlen = $inFile->read($inbuf, $bufferLength))) {
+ if ($inlen < $bufferLength) {
+ $inbuf .= "\x00" x ($bufferLength - $inlen);
+ }
+
+ my $outbuf = $ecb->decrypt($inbuf, $key);
+ if ($fileSize < $inlen) {
+ $inlen = $fileSize;
+ }
+
+ $outFile->write($outbuf, $inlen);
+ $i++;
+ $fileSize -= $inlen;
+ }
+}
+
+sub _generateDecryptionKey {
+ my $self = shift;
+ my ($blockKey) = @_;
+
+ my $hash;
+ unless ($self->{pregeneratedKey}) {
+ $hash = $self->{hashProc}->($self->{salt} . Encode::encode('UTF-16LE', $self->{password}));
+ for (my $i = 0; $i < $self->{spinCount}; $i++) {
+ $hash = $self->{hashProc}->(pack('L', $i) . $hash);
+ }
+ $self->{pregeneratedKey} = $hash;
+ }
+
+ $hash = $self->{hashProc}->($self->{pregeneratedKey} . $blockKey);
+
+ my $x1 = $self->{hashProc}->(("\x36" x 64) ^ $hash);
+ if (length($x1) >= $self->{keyLength}) {
+ $hash = substr($x1, 0, $self->{keyLength});
+ } else {
+ my $x2 = $self->{hashProc}->(("\x5C" x 64) ^ $hash);
+ $hash = substr($x1 . $x2, 0, $self->{keyLength});
+ }
+
+ return $hash;
+}
+
+sub verifyPassword {
+ my $self = shift;
+
+ my ($encryptedVerifier, $encryptedVerifierHash) = @_;
+
+ my $verifier = $self->decrypt($encryptedVerifier);
+ my $verifierHash = $self->decrypt($encryptedVerifierHash);
+
+ my $verifierHash0 = $self->{hashProc}->($verifier);
+
+ die "Wrong password: $self" unless ($verifierHash0 eq substr($verifierHash, 0, length($verifierHash0)));
+}
+
+1;