Initial Commit
This commit is contained in:
242
database/perl/vendor/lib/Spreadsheet/ParseXLSX/Decryptor.pm
vendored
Normal file
242
database/perl/vendor/lib/Spreadsheet/ParseXLSX/Decryptor.pm
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
package Spreadsheet::ParseXLSX::Decryptor;
|
||||
our $AUTHORITY = 'cpan:DOY';
|
||||
$Spreadsheet::ParseXLSX::Decryptor::VERSION = '0.27';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Crypt::Mode::CBC;
|
||||
use Crypt::Mode::ECB;
|
||||
use Digest::SHA ();
|
||||
use Encode ();
|
||||
use File::Temp ();
|
||||
use MIME::Base64 ();
|
||||
use OLE::Storage_Lite;
|
||||
|
||||
use Spreadsheet::ParseXLSX::Decryptor::Standard;
|
||||
use Spreadsheet::ParseXLSX::Decryptor::Agile;
|
||||
|
||||
sub open {
|
||||
my $class = shift;
|
||||
|
||||
my ($filename, $password) = @_;
|
||||
|
||||
$password = $password || 'VelvetSweatshop';
|
||||
|
||||
my ($infoFH, $packageFH) = $class->_getCompoundData(
|
||||
$filename,
|
||||
['EncryptionInfo', 'EncryptedPackage']
|
||||
);
|
||||
|
||||
return unless $infoFH;
|
||||
|
||||
my $buffer;
|
||||
$infoFH->read($buffer, 8);
|
||||
my ($majorVers, $minorVers) = unpack('s<s<', $buffer);
|
||||
|
||||
my $xlsx;
|
||||
if ($majorVers == 4 && $minorVers == 4) {
|
||||
$xlsx = $class->_agileDecryption($infoFH, $packageFH, $password);
|
||||
} else {
|
||||
$xlsx = $class->_standardDecryption($infoFH, $packageFH, $password);
|
||||
}
|
||||
|
||||
return $xlsx;
|
||||
}
|
||||
|
||||
sub _getCompoundData {
|
||||
my $class = shift;
|
||||
my ($filename, $names) = @_;
|
||||
|
||||
my @files;
|
||||
|
||||
my $storage = OLE::Storage_Lite->new($filename);
|
||||
|
||||
foreach my $name (@{$names}) {
|
||||
my @data = $storage->getPpsSearch([OLE::Storage_Lite::Asc2Ucs($name)], 1, 1);
|
||||
if ($#data < 0) {
|
||||
push @files, undef;
|
||||
} else {
|
||||
my $fh = File::Temp->new;
|
||||
binmode($fh);
|
||||
$fh->write($data[0]->{Data});
|
||||
$fh->seek(0, 0);
|
||||
push @files, $fh;
|
||||
}
|
||||
}
|
||||
|
||||
return @files;
|
||||
}
|
||||
|
||||
sub _standardDecryption {
|
||||
my $class = shift;
|
||||
my ($infoFH, $packageFH, $password) = @_;
|
||||
|
||||
my $buffer;
|
||||
my $n = $infoFH->read($buffer, 24);
|
||||
|
||||
my ($encryptionHeaderSize, undef, undef, $algID, $algIDHash, $keyBits) = unpack('L<*', $buffer);
|
||||
|
||||
$infoFH->seek($encryptionHeaderSize - 0x14, IO::File::SEEK_CUR);
|
||||
|
||||
$infoFH->read($buffer, 4);
|
||||
|
||||
my $saltSize = unpack('L<', $buffer);
|
||||
|
||||
my ($salt, $encryptedVerifier, $verifierHashSize, $encryptedVerifierHash);
|
||||
|
||||
$infoFH->read($salt, 16);
|
||||
$infoFH->read($encryptedVerifier, 16);
|
||||
|
||||
$infoFH->read($buffer, 4);
|
||||
$verifierHashSize = unpack('L<', $buffer);
|
||||
|
||||
$infoFH->read($encryptedVerifierHash, 32);
|
||||
$infoFH->close();
|
||||
|
||||
my ($cipherAlgorithm, $hashAlgorithm);
|
||||
|
||||
if ($algID == 0x0000660E || $algID == 0x0000660F || $algID == 0x0000660E) {
|
||||
$cipherAlgorithm = 'AES';
|
||||
} else {
|
||||
die sprintf('Unsupported encryption algorithm: 0x%.8x', $algID);
|
||||
}
|
||||
|
||||
if ($algIDHash == 0x00008004) {
|
||||
$hashAlgorithm = 'SHA-1';
|
||||
} else {
|
||||
die sprintf('Unsupported hash algorithm: 0x%.8x', $algIDHash);
|
||||
}
|
||||
|
||||
my $decryptor = Spreadsheet::ParseXLSX::Decryptor::Standard->new({
|
||||
cipherAlgorithm => $cipherAlgorithm,
|
||||
cipherChaining => 'ECB',
|
||||
hashAlgorithm => $hashAlgorithm,
|
||||
salt => $salt,
|
||||
password => $password,
|
||||
keyBits => $keyBits,
|
||||
spinCount => 50000
|
||||
});
|
||||
|
||||
$decryptor->verifyPassword($encryptedVerifier, $encryptedVerifierHash);
|
||||
|
||||
my $fh = File::Temp->new;
|
||||
binmode($fh);
|
||||
|
||||
my $inbuf;
|
||||
$packageFH->read($inbuf, 8);
|
||||
my $fileSize = unpack('L<', $inbuf);
|
||||
|
||||
$decryptor->decryptFile($packageFH, $fh, 1024, $fileSize);
|
||||
|
||||
$fh->seek(0, 0);
|
||||
|
||||
return $fh;
|
||||
}
|
||||
|
||||
sub _agileDecryption {
|
||||
my $class = shift;
|
||||
my ($infoFH, $packageFH, $password) = @_;
|
||||
|
||||
my $xml = XML::Twig->new;
|
||||
$xml->parse($infoFH);
|
||||
|
||||
my ($info) = $xml->find_nodes('//encryption/keyEncryptors/keyEncryptor/p:encryptedKey');
|
||||
|
||||
my $encryptedVerifierHashInput = MIME::Base64::decode($info->att('encryptedVerifierHashInput'));
|
||||
my $encryptedVerifierHashValue = MIME::Base64::decode($info->att('encryptedVerifierHashValue'));
|
||||
my $encryptedKeyValue = MIME::Base64::decode($info->att('encryptedKeyValue'));
|
||||
|
||||
my $keyDecryptor = Spreadsheet::ParseXLSX::Decryptor::Agile->new({
|
||||
cipherAlgorithm => $info->att('cipherAlgorithm'),
|
||||
cipherChaining => $info->att('cipherChaining'),
|
||||
hashAlgorithm => $info->att('hashAlgorithm'),
|
||||
salt => MIME::Base64::decode($info->att('saltValue')),
|
||||
password => $password,
|
||||
keyBits => 0 + $info->att('keyBits'),
|
||||
spinCount => 0 + $info->att('spinCount'),
|
||||
blockSize => 0 + $info->att('blockSize')
|
||||
});
|
||||
|
||||
$keyDecryptor->verifyPassword($encryptedVerifierHashInput, $encryptedVerifierHashValue);
|
||||
|
||||
my $key = $keyDecryptor->decrypt($encryptedKeyValue, "\x14\x6e\x0b\xe7\xab\xac\xd0\xd6");
|
||||
|
||||
($info) = $xml->find_nodes('//encryption/keyData');
|
||||
|
||||
my $fileDecryptor = Spreadsheet::ParseXLSX::Decryptor::Agile->new({
|
||||
cipherAlgorithm => $info->att('cipherAlgorithm'),
|
||||
cipherChaining => $info->att('cipherChaining'),
|
||||
hashAlgorithm => $info->att('hashAlgorithm'),
|
||||
salt => MIME::Base64::decode($info->att('saltValue')),
|
||||
password => $password,
|
||||
keyBits => 0 + $info->att('keyBits'),
|
||||
blockSize => 0 + $info->att('blockSize')
|
||||
});
|
||||
|
||||
my $fh = File::Temp->new;
|
||||
binmode($fh);
|
||||
|
||||
my $inbuf;
|
||||
$packageFH->read($inbuf, 8);
|
||||
my $fileSize = unpack('L<', $inbuf);
|
||||
|
||||
$fileDecryptor->decryptFile($packageFH, $fh, 4096, $key, $fileSize);
|
||||
|
||||
$fh->seek(0, 0);
|
||||
|
||||
return $fh;
|
||||
}
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($args) = @_;
|
||||
|
||||
my $self = { %$args };
|
||||
$self->{keyLength} = $self->{keyBits} / 8;
|
||||
|
||||
if ($self->{hashAlgorithm} eq 'SHA512') {
|
||||
$self->{hashProc} = \&Digest::SHA::sha512;
|
||||
} elsif ($self->{hashAlgorithm} eq 'SHA-1') {
|
||||
$self->{hashProc} = \&Digest::SHA::sha1;
|
||||
} elsif ($self->{hashAlgorithm} eq 'SHA256') {
|
||||
$self->{hashProc} = \&Digest::SHA::sha256;
|
||||
} else {
|
||||
die "Unsupported hash algorithm: $self->{hashAlgorithm}";
|
||||
}
|
||||
|
||||
return bless $self, $class;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Spreadsheet::ParseXLSX::Decryptor
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 0.27
|
||||
|
||||
=for Pod::Coverage new
|
||||
open
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Jesse Luehrs <doy@tozt.net>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
This software is Copyright (c) 2016 by Jesse Luehrs.
|
||||
|
||||
This is free software, licensed under:
|
||||
|
||||
The MIT (X11) License
|
||||
|
||||
=cut
|
||||
135
database/perl/vendor/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm
vendored
Normal file
135
database/perl/vendor/lib/Spreadsheet/ParseXLSX/Decryptor/Agile.pm
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package Spreadsheet::ParseXLSX::Decryptor::Agile;
|
||||
our $AUTHORITY = 'cpan:DOY';
|
||||
$Spreadsheet::ParseXLSX::Decryptor::Agile::VERSION = '0.27';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'Spreadsheet::ParseXLSX::Decryptor';
|
||||
|
||||
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;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Spreadsheet::ParseXLSX::Decryptor::Agile
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 0.27
|
||||
|
||||
=for Pod::Coverage decrypt
|
||||
decryptFile
|
||||
verifyPassword
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Jesse Luehrs <doy@tozt.net>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
This software is Copyright (c) 2016 by Jesse Luehrs.
|
||||
|
||||
This is free software, licensed under:
|
||||
|
||||
The MIT (X11) License
|
||||
|
||||
=cut
|
||||
115
database/perl/vendor/lib/Spreadsheet/ParseXLSX/Decryptor/Standard.pm
vendored
Normal file
115
database/perl/vendor/lib/Spreadsheet/ParseXLSX/Decryptor/Standard.pm
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
package Spreadsheet::ParseXLSX::Decryptor::Standard;
|
||||
our $AUTHORITY = 'cpan:DOY';
|
||||
$Spreadsheet::ParseXLSX::Decryptor::Standard::VERSION = '0.27';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'Spreadsheet::ParseXLSX::Decryptor';
|
||||
|
||||
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;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Spreadsheet::ParseXLSX::Decryptor::Standard
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 0.27
|
||||
|
||||
=for Pod::Coverage decrypt
|
||||
decryptFile
|
||||
verifyPassword
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Jesse Luehrs <doy@tozt.net>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
This software is Copyright (c) 2016 by Jesse Luehrs.
|
||||
|
||||
This is free software, licensed under:
|
||||
|
||||
The MIT (X11) License
|
||||
|
||||
=cut
|
||||
Reference in New Issue
Block a user