← Index
NYTProf Performance Profile   « line view »
For t/bug-md-11.t
  Run on Fri Mar 8 13:27:24 2024
Reported on Fri Mar 8 13:30:23 2024

Filename/home/micha/Projekt/spreadsheet-parsexlsx/lib/Spreadsheet/ParseXLSX/Decryptor.pm
StatementsExecuted 23 statements in 974µs
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
1111.36ms1.66msSpreadsheet::ParseXLSX::Decryptor::::BEGIN@12Spreadsheet::ParseXLSX::Decryptor::BEGIN@12
111410µs490µsSpreadsheet::ParseXLSX::Decryptor::::BEGIN@19Spreadsheet::ParseXLSX::Decryptor::BEGIN@19
111369µs453µsSpreadsheet::ParseXLSX::Decryptor::::BEGIN@18Spreadsheet::ParseXLSX::Decryptor::BEGIN@18
111215µs348µsSpreadsheet::ParseXLSX::Decryptor::::BEGIN@15Spreadsheet::ParseXLSX::Decryptor::BEGIN@15
111183µs228µsSpreadsheet::ParseXLSX::Decryptor::::BEGIN@11Spreadsheet::ParseXLSX::Decryptor::BEGIN@11
111178µs9.84msSpreadsheet::ParseXLSX::Decryptor::::BEGIN@10Spreadsheet::ParseXLSX::Decryptor::BEGIN@10
11113µs15µsSpreadsheet::ParseXLSX::Decryptor::::BEGIN@3Spreadsheet::ParseXLSX::Decryptor::BEGIN@3
1118µs18µsSpreadsheet::ParseXLSX::Decryptor::::BEGIN@16Spreadsheet::ParseXLSX::Decryptor::BEGIN@16
1114µs25µsSpreadsheet::ParseXLSX::Decryptor::::BEGIN@4Spreadsheet::ParseXLSX::Decryptor::BEGIN@4
1113µs3µsSpreadsheet::ParseXLSX::Decryptor::::BEGIN@13Spreadsheet::ParseXLSX::Decryptor::BEGIN@13
1111µs1µsSpreadsheet::ParseXLSX::Decryptor::::BEGIN@14Spreadsheet::ParseXLSX::Decryptor::BEGIN@14
0000s0sSpreadsheet::ParseXLSX::Decryptor::::_agileDecryptionSpreadsheet::ParseXLSX::Decryptor::_agileDecryption
0000s0sSpreadsheet::ParseXLSX::Decryptor::::_getCompoundDataSpreadsheet::ParseXLSX::Decryptor::_getCompoundData
0000s0sSpreadsheet::ParseXLSX::Decryptor::::_standardDecryptionSpreadsheet::ParseXLSX::Decryptor::_standardDecryption
0000s0sSpreadsheet::ParseXLSX::Decryptor::::newSpreadsheet::ParseXLSX::Decryptor::new
0000s0sSpreadsheet::ParseXLSX::Decryptor::::openSpreadsheet::ParseXLSX::Decryptor::open
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1package Spreadsheet::ParseXLSX::Decryptor;
2
3219µs217µs
# spent 15µs (13+2) within Spreadsheet::ParseXLSX::Decryptor::BEGIN@3 which was called: # once (13µs+2µs) by Spreadsheet::ParseXLSX::BEGIN@17 at line 3
use strict;
# spent 15µs making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@3 # spent 2µs making 1 call to strict::import
4217µs246µs
# spent 25µs (4+21) within Spreadsheet::ParseXLSX::Decryptor::BEGIN@4 which was called: # once (4µs+21µs) by Spreadsheet::ParseXLSX::BEGIN@17 at line 4
use warnings;
# spent 25µs making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@4 # spent 21µs making 1 call to warnings::import
5
6# VERSION
7
8# ABSTRACT: helper class to open password protected files
9
10264µs29.84ms
# spent 9.84ms (178µs+9.66) within Spreadsheet::ParseXLSX::Decryptor::BEGIN@10 which was called: # once (178µs+9.66ms) by Spreadsheet::ParseXLSX::BEGIN@17 at line 10
use Crypt::Mode::CBC;
# spent 9.84ms making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@10 # spent 800ns making 1 call to UNIVERSAL::import
11267µs2228µs
# spent 228µs (183+44) within Spreadsheet::ParseXLSX::Decryptor::BEGIN@11 which was called: # once (183µs+44µs) by Spreadsheet::ParseXLSX::BEGIN@17 at line 11
use Crypt::Mode::ECB;
# spent 228µs making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@11 # spent 900ns making 1 call to UNIVERSAL::import
12280µs11.66ms
# spent 1.66ms (1.36+297µs) within Spreadsheet::ParseXLSX::Decryptor::BEGIN@12 which was called: # once (1.36ms+297µs) by Spreadsheet::ParseXLSX::BEGIN@17 at line 12
use Digest::SHA ();
# spent 1.66ms making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@12
13211µs13µs
# spent 3µs within Spreadsheet::ParseXLSX::Decryptor::BEGIN@13 which was called: # once (3µs+0s) by Spreadsheet::ParseXLSX::BEGIN@17 at line 13
use Encode ();
# spent 3µs making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@13
1428µs11µs
# spent 1µs within Spreadsheet::ParseXLSX::Decryptor::BEGIN@14 which was called: # once (1µs+0s) by Spreadsheet::ParseXLSX::BEGIN@17 at line 14
use File::Temp ();
# spent 1µs making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@14
15274µs1348µs
# spent 348µs (215+133) within Spreadsheet::ParseXLSX::Decryptor::BEGIN@15 which was called: # once (215µs+133µs) by Spreadsheet::ParseXLSX::BEGIN@17 at line 15
use MIME::Base64 ();
# spent 348µs making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@15
16218µs229µs
# spent 18µs (8+11) within Spreadsheet::ParseXLSX::Decryptor::BEGIN@16 which was called: # once (8µs+11µs) by Spreadsheet::ParseXLSX::BEGIN@17 at line 16
use OLE::Storage_Lite;
# spent 18µs making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@16 # spent 11µs making 1 call to Exporter::import
17
18264µs2454µs
# spent 453µs (369+84) within Spreadsheet::ParseXLSX::Decryptor::BEGIN@18 which was called: # once (369µs+84µs) by Spreadsheet::ParseXLSX::BEGIN@17 at line 18
use Spreadsheet::ParseXLSX::Decryptor::Standard;
# spent 453µs making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@18 # spent 1µs making 1 call to UNIVERSAL::import
192549µs2492µs
# spent 490µs (410+81) within Spreadsheet::ParseXLSX::Decryptor::BEGIN@19 which was called: # once (410µs+81µs) by Spreadsheet::ParseXLSX::BEGIN@17 at line 19
use Spreadsheet::ParseXLSX::Decryptor::Agile;
# spent 490µs making 1 call to Spreadsheet::ParseXLSX::Decryptor::BEGIN@19 # spent 1µs making 1 call to UNIVERSAL::import
20
21sub open {
22 my $class = shift;
23
24 my ($filename, $password) = @_;
25
26 $password = $password || 'VelvetSweatshop';
27
28 my ($infoFH, $packageFH) = $class->_getCompoundData($filename, ['EncryptionInfo', 'EncryptedPackage']);
29
30 return unless $infoFH;
31
32 my $buffer;
33 $infoFH->read($buffer, 8);
34 my ($majorVers, $minorVers) = unpack('s<s<', $buffer);
35
36 my $xlsx;
37 if ($majorVers == 4 && $minorVers == 4) {
38 $xlsx = $class->_agileDecryption($infoFH, $packageFH, $password);
39 } else {
40 $xlsx = $class->_standardDecryption($infoFH, $packageFH, $password);
41 }
42
43 return $xlsx;
44}
45
46sub _getCompoundData {
47 my $class = shift;
48 my ($filename, $names) = @_;
49
50 my @files;
51
52 my $storage = OLE::Storage_Lite->new($filename);
53
54 foreach my $name (@{$names}) {
55 my @data = $storage->getPpsSearch([OLE::Storage_Lite::Asc2Ucs($name)], 1, 1);
56 if ($#data < 0) {
57 push @files, undef;
58 } else {
59 my $fh = File::Temp->new;
60 binmode($fh);
61 $fh->write($data[0]->{Data});
62 $fh->seek(0, 0);
63 push @files, $fh;
64 }
65 }
66
67 return @files;
68}
69
70sub _standardDecryption {
71 my $class = shift;
72 my ($infoFH, $packageFH, $password) = @_;
73
74 my $buffer;
75 my $n = $infoFH->read($buffer, 24);
76
77 my ($encryptionHeaderSize, undef, undef, $algID, $algIDHash, $keyBits) = unpack('L<*', $buffer);
78
79 $infoFH->seek($encryptionHeaderSize - 0x14, IO::File::SEEK_CUR);
80
81 $infoFH->read($buffer, 4);
82
83 my $saltSize = unpack('L<', $buffer);
84
85 my ($salt, $encryptedVerifier, $verifierHashSize, $encryptedVerifierHash);
86
87 $infoFH->read($salt, 16);
88 $infoFH->read($encryptedVerifier, 16);
89
90 $infoFH->read($buffer, 4);
91 $verifierHashSize = unpack('L<', $buffer);
92
93 $infoFH->read($encryptedVerifierHash, 32);
94 $infoFH->close();
95
96 my ($cipherAlgorithm, $hashAlgorithm);
97
98 if ($algID == 0x0000660E || $algID == 0x0000660F || $algID == 0x0000660E) {
99 $cipherAlgorithm = 'AES';
100 } else {
101 die sprintf('Unsupported encryption algorithm: 0x%.8x', $algID);
102 }
103
104 if ($algIDHash == 0x00008004) {
105 $hashAlgorithm = 'SHA-1';
106 } else {
107 die sprintf('Unsupported hash algorithm: 0x%.8x', $algIDHash);
108 }
109
110 my $decryptor = Spreadsheet::ParseXLSX::Decryptor::Standard->new({
111 cipherAlgorithm => $cipherAlgorithm,
112 cipherChaining => 'ECB',
113 hashAlgorithm => $hashAlgorithm,
114 salt => $salt,
115 password => $password,
116 keyBits => $keyBits,
117 spinCount => 50000
118 }
119 );
120
121 $decryptor->verifyPassword($encryptedVerifier, $encryptedVerifierHash);
122
123 my $fh = File::Temp->new;
124 binmode($fh);
125
126 my $inbuf;
127 $packageFH->read($inbuf, 8);
128 my $fileSize = unpack('L<', $inbuf);
129
130 $decryptor->decryptFile($packageFH, $fh, 1024, $fileSize);
131
132 $fh->seek(0, 0);
133
134 return $fh;
135}
136
137sub _agileDecryption {
138 my $class = shift;
139 my ($infoFH, $packageFH, $password) = @_;
140
141 my $xml = XML::Twig->new;
142 $xml->parse($infoFH);
143
144 my ($info) = $xml->find_nodes('//encryption/keyEncryptors/keyEncryptor/p:encryptedKey');
145
146 my $encryptedVerifierHashInput = MIME::Base64::decode($info->att('encryptedVerifierHashInput'));
147 my $encryptedVerifierHashValue = MIME::Base64::decode($info->att('encryptedVerifierHashValue'));
148 my $encryptedKeyValue = MIME::Base64::decode($info->att('encryptedKeyValue'));
149 my $hashSize = 0 + $info->att('hashSize');
150
151 my $keyDecryptor = Spreadsheet::ParseXLSX::Decryptor::Agile->new({
152 cipherAlgorithm => $info->att('cipherAlgorithm'),
153 cipherChaining => $info->att('cipherChaining'),
154 hashAlgorithm => $info->att('hashAlgorithm'),
155 salt => MIME::Base64::decode($info->att('saltValue')),
156 password => $password,
157 keyBits => 0 + $info->att('keyBits'),
158 spinCount => 0 + $info->att('spinCount'),
159 blockSize => 0 + $info->att('blockSize')
160 }
161 );
162
163 $keyDecryptor->verifyPassword($encryptedVerifierHashInput, $encryptedVerifierHashValue, $hashSize);
164
165 my $key = $keyDecryptor->decrypt($encryptedKeyValue, "\x14\x6e\x0b\xe7\xab\xac\xd0\xd6");
166
167 ($info) = $xml->find_nodes('//encryption/keyData');
168
169 my $fileDecryptor = Spreadsheet::ParseXLSX::Decryptor::Agile->new({
170 cipherAlgorithm => $info->att('cipherAlgorithm'),
171 cipherChaining => $info->att('cipherChaining'),
172 hashAlgorithm => $info->att('hashAlgorithm'),
173 salt => MIME::Base64::decode($info->att('saltValue')),
174 password => $password,
175 keyBits => 0 + $info->att('keyBits'),
176 blockSize => 0 + $info->att('blockSize')
177 }
178 );
179
180 my $fh = File::Temp->new;
181 binmode($fh);
182
183 my $inbuf;
184 $packageFH->read($inbuf, 8);
185 my $fileSize = unpack('L<', $inbuf);
186
187 $fileDecryptor->decryptFile($packageFH, $fh, 4096, $key, $fileSize);
188
189 $fh->seek(0, 0);
190
191 return $fh;
192}
193
194sub new {
195 my $class = shift;
196 my ($args) = @_;
197
198 my $self = {%$args};
199 $self->{keyLength} = $self->{keyBits} / 8;
200
201 if ($self->{hashAlgorithm} eq 'SHA512') {
202 $self->{hashProc} = \&Digest::SHA::sha512;
203 } elsif (($self->{hashAlgorithm} eq 'SHA-1') || ($self->{hashAlgorithm} eq 'SHA1')) {
204 $self->{hashProc} = \&Digest::SHA::sha1;
205 } elsif ($self->{hashAlgorithm} eq 'SHA256') {
206 $self->{hashProc} = \&Digest::SHA::sha256;
207 } else {
208 die "Unsupported hash algorithm: $self->{hashAlgorithm}";
209 }
210
211 return bless $self, $class;
212}
213
214=begin Pod::Coverage
215
216 new
217 open
218
219=end Pod::Coverage
220
221=cut
222
22312µs1;