summaryrefslogtreecommitdiffstats
path: root/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm')
-rw-r--r--lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm107
1 files changed, 107 insertions, 0 deletions
diff --git a/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm b/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm
new file mode 100644
index 0000000..3b836e3
--- /dev/null
+++ b/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm
@@ -0,0 +1,107 @@
+package Spreadsheet::ParseXLSX::Decryptor::Agile;
+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, $blockKey) = @_;
+
+ my $key = $self->_generateDecryptionKey($blockKey);
+ my $iv = $self->_generateInitializationVector('', $self->{blockSize});
+ my $cbc = Crypt::Mode::CBC->new($self->{cipherAlgorithm}, 0);
+ return $cbc->decrypt($encryptedValue, $key, $iv);
+}
+
+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);
+
+ if (length($hash) > $self->{keyLength}) {
+ $hash = substr($hash, 0, $self->{keyLength});
+ } elsif (length($hash) < $self->{keyLength}) {
+ $hash .= "\x36" x ($self->{keyLength} - length($hash));
+ }
+ return $hash;
+}
+
+sub _generateInitializationVector {
+ my $self = shift;
+ my ($blockKey, $blockSize) = @_;
+
+ my $iv;
+ if ($blockKey) {
+ $iv = $self->{hashProc}->($self->{salt} . $blockKey);
+ } else {
+ $iv = $self->{salt};
+ }
+
+ if (length($iv) > $blockSize) {
+ $iv = substr($iv, 0, $blockSize);
+ } elsif (length($iv) < $blockSize) {
+ $iv = $iv . ("\x36" x ($blockSize - length($iv)));
+ }
+
+ return $iv;
+}
+
+sub decryptFile {
+ my $self = shift;
+ my ($inFile, $outFile, $bufferLength, $key, $fileSize) = @_;
+
+ my $cbc = Crypt::Mode::CBC->new($self->{cipherAlgorithm}, 0);
+
+ my $inbuf;
+ my $i = 0;
+
+ while (($fileSize > 0) && (my $inlen = $inFile->read($inbuf, $bufferLength))) {
+ my $blockId = pack('L', $i);
+
+ my $iv = $self->_generateInitializationVector($blockId, $self->{blockSize});
+
+ if ($inlen < $bufferLength) {
+ $inbuf .= "\x00" x ($bufferLength - $inlen);
+ }
+
+ my $outbuf = $cbc->decrypt($inbuf, $key, $iv);
+ if ($fileSize < $inlen) {
+ $inlen = $fileSize;
+ }
+
+ $outFile->write($outbuf, $inlen);
+ $i++;
+ $fileSize -= $inlen;
+ }
+}
+
+sub verifyPassword {
+ my $self = shift;
+
+ my ($encryptedVerifier, $encryptedVerifierHash) = @_;
+
+ 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");
+
+ die "Wrong password: $self" unless ($encryptedVerifierHash0 eq $encryptedVerifierHash);
+}
+
+1;