diff options
Diffstat (limited to 'lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm')
-rw-r--r-- | lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm | 107 |
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; |