1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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;
|