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