| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package CAM::PDF::Decrypt; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# These are included at runtime via eval below |
|
4
|
|
|
|
|
|
|
# use Digest::MD5; |
|
5
|
|
|
|
|
|
|
# use Crypt::RC4; |
|
6
|
|
|
|
|
|
|
|
|
7
|
3
|
|
|
3
|
|
51
|
use 5.006; |
|
|
3
|
|
|
|
|
9
|
|
|
|
3
|
|
|
|
|
112
|
|
|
8
|
3
|
|
|
3
|
|
15
|
use warnings; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
87
|
|
|
9
|
3
|
|
|
3
|
|
19
|
use strict; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
77
|
|
|
10
|
3
|
|
|
3
|
|
15
|
use Carp; |
|
|
3
|
|
|
|
|
4
|
|
|
|
3
|
|
|
|
|
220
|
|
|
11
|
3
|
|
|
3
|
|
15
|
use English qw(-no_match_vars); |
|
|
3
|
|
|
|
|
4
|
|
|
|
3
|
|
|
|
|
15
|
|
|
12
|
3
|
|
|
3
|
|
1406
|
use CAM::PDF; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
65
|
|
|
13
|
3
|
|
|
3
|
|
14
|
use CAM::PDF::Node; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
8657
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our $VERSION = '1.60'; |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=for stopwords Decrypt |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 NAME |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
CAM::PDF::Decrypt - PDF security helper |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 LICENSE |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
See CAM::PDF. |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
use CAM::PDF; |
|
30
|
|
|
|
|
|
|
my $pdf = CAM::PDF->new($filename); |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
This class is used invisibly by CAM::PDF whenever it detects that a |
|
35
|
|
|
|
|
|
|
document is encrypted. See new(), getPrefs() and setPrefs() in that |
|
36
|
|
|
|
|
|
|
module. |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=cut |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# These constants come from the Adobe PDF Reference document, IIRC |
|
41
|
|
|
|
|
|
|
my $padding = pack 'C*', |
|
42
|
|
|
|
|
|
|
0x28, 0xbf, 0x4e, 0x5e, |
|
43
|
|
|
|
|
|
|
0x4e, 0x75, 0x8a, 0x41, |
|
44
|
|
|
|
|
|
|
0x64, 0x00, 0x4e, 0x56, |
|
45
|
|
|
|
|
|
|
0xff, 0xfa, 0x01, 0x08, |
|
46
|
|
|
|
|
|
|
0x2e, 0x2e, 0x00, 0xb6, |
|
47
|
|
|
|
|
|
|
0xd0, 0x68, 0x3e, 0x80, |
|
48
|
|
|
|
|
|
|
0x2f, 0x0c, 0xa9, 0xfe, |
|
49
|
|
|
|
|
|
|
0x64, 0x53, 0x69, 0x7a; |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=over |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=item $pkg->new($pdf, $ownerpass, $userpass, $prompt) |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Create and validate a new decryption object. If this fails, it will |
|
59
|
|
|
|
|
|
|
set $CAM::PDF::errstr and return undef. |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
C<$prompt> is a boolean that says whether the user should be prompted for |
|
62
|
|
|
|
|
|
|
a password on the command line. |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=cut |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub new |
|
67
|
|
|
|
|
|
|
{ |
|
68
|
38
|
|
|
38
|
1
|
80
|
my $pkg = shift; |
|
69
|
38
|
|
|
|
|
74
|
my $doc = shift; |
|
70
|
38
|
|
|
|
|
75
|
my $opassword = shift; |
|
71
|
38
|
|
|
|
|
66
|
my $upassword = shift; |
|
72
|
38
|
|
|
|
|
65
|
my $prompt = shift; |
|
73
|
|
|
|
|
|
|
|
|
74
|
38
|
50
|
|
|
|
138
|
if (!$doc) |
|
75
|
|
|
|
|
|
|
{ |
|
76
|
0
|
|
|
|
|
0
|
$CAM::PDF::errstr = "This is an invalid PDF doc\n"; |
|
77
|
0
|
|
|
|
|
0
|
return; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
|
|
80
|
38
|
50
|
|
|
|
130
|
if (!exists $doc->{trailer}) |
|
81
|
|
|
|
|
|
|
{ |
|
82
|
0
|
|
|
|
|
0
|
$CAM::PDF::errstr = "This PDF doc has no trailer\n"; |
|
83
|
0
|
|
|
|
|
0
|
return; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
|
|
86
|
38
|
|
|
|
|
204
|
my $self = bless { |
|
87
|
|
|
|
|
|
|
keycache => {}, |
|
88
|
|
|
|
|
|
|
}, $pkg; |
|
89
|
|
|
|
|
|
|
|
|
90
|
38
|
100
|
|
|
|
166
|
if (!exists $doc->{trailer}->{Encrypt}) |
|
91
|
|
|
|
|
|
|
{ |
|
92
|
|
|
|
|
|
|
# This PDF doc is not encrypted. Return an empty object |
|
93
|
10
|
|
|
|
|
32
|
$self->{noop} = 1; |
|
94
|
10
|
|
|
|
|
45
|
return $self; |
|
95
|
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
|
|
97
|
28
|
50
|
|
|
|
84
|
if (!$doc->{ID}) |
|
98
|
|
|
|
|
|
|
{ |
|
99
|
0
|
|
|
|
|
0
|
$CAM::PDF::errstr = "This PDF lacks an ID. The document cannot be decrypted.\n"; |
|
100
|
0
|
|
|
|
|
0
|
return; |
|
101
|
|
|
|
|
|
|
} |
|
102
|
|
|
|
|
|
|
|
|
103
|
28
|
|
|
|
|
121
|
return $self->_init($doc, $opassword, $upassword, $prompt); |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub _init |
|
107
|
|
|
|
|
|
|
{ |
|
108
|
28
|
|
|
28
|
|
41
|
my $self = shift; |
|
109
|
28
|
|
|
|
|
115
|
my $doc = shift; |
|
110
|
28
|
|
|
|
|
54
|
my $opassword = shift; |
|
111
|
28
|
|
|
|
|
50
|
my $upassword = shift; |
|
112
|
28
|
|
|
|
|
44
|
my $prompt = shift; |
|
113
|
|
|
|
|
|
|
|
|
114
|
28
|
50
|
|
|
|
134
|
if ($doc->{trailer}->{Encrypt}->{type} eq 'reference') |
|
115
|
|
|
|
|
|
|
{ |
|
116
|
|
|
|
|
|
|
# If the encryption block is an indirect reference, store |
|
117
|
|
|
|
|
|
|
# it's location so we don't accidentally encrypt it. |
|
118
|
28
|
|
|
|
|
108
|
$self->{EncryptBlock} = $doc->{trailer}->{Encrypt}->{value}; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
|
|
121
|
28
|
|
|
|
|
118
|
my $dict = $doc->getValue($doc->{trailer}->{Encrypt}); |
|
122
|
|
|
|
|
|
|
|
|
123
|
28
|
50
|
33
|
|
|
237
|
if ($dict->{Filter}->{value} ne 'Standard' || ($dict->{V}->{value} != 1 && $dict->{V}->{value} != 2)) |
|
|
|
|
33
|
|
|
|
|
|
124
|
|
|
|
|
|
|
{ |
|
125
|
0
|
|
|
|
|
0
|
$CAM::PDF::errstr = "CAM::PDF only supports Version 1 and 2 of the Standard encryption filter. This PDF uses something else.\n"; |
|
126
|
0
|
|
|
|
|
0
|
return; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# V == 1 means MD5+RC4, keylength == 40 |
|
130
|
|
|
|
|
|
|
# V == 2 means MD5+RC4, keylength == {40, 48, 56, 64, ..., 128} |
|
131
|
|
|
|
|
|
|
# See PDF Ref 1.5 p93 |
|
132
|
28
|
0
|
|
|
|
110
|
$self->{keylength} = $dict->{V}->{value} == 1 ? 40 : $dict->{Length} ? $dict->{Length}->{value} : 40; |
|
|
|
50
|
|
|
|
|
|
|
133
|
28
|
50
|
33
|
|
|
246
|
if (40 > $self->{keylength} || 128 < $self->{keylength} || 0 != $self->{keylength} % 8) |
|
|
|
|
33
|
|
|
|
|
|
134
|
|
|
|
|
|
|
{ |
|
135
|
0
|
|
|
|
|
0
|
$CAM::PDF::errstr = "Invalid key length $self->{keylength}. The document cannot be decrypted.\n"; |
|
136
|
0
|
|
|
|
|
0
|
return; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# PDF Ref 1.5 pp. 97-98 |
|
140
|
28
|
|
|
|
|
76
|
foreach my $key ('R', 'O', 'U', 'P') |
|
141
|
|
|
|
|
|
|
{ |
|
142
|
112
|
50
|
|
|
|
209
|
if (exists $dict->{$key}) |
|
143
|
|
|
|
|
|
|
{ |
|
144
|
112
|
|
|
|
|
326
|
$self->{$key} = $dict->{$key}->{value}; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
else |
|
147
|
|
|
|
|
|
|
{ |
|
148
|
0
|
|
|
|
|
0
|
$CAM::PDF::errstr = "Requred decryption datum $key is missing. The document cannot be decrypted.\n"; |
|
149
|
0
|
|
|
|
|
0
|
return; |
|
150
|
|
|
|
|
|
|
} |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
28
|
|
|
|
|
303
|
require Digest::MD5; |
|
154
|
28
|
|
|
|
|
127
|
require Crypt::RC4; |
|
155
|
|
|
|
|
|
|
|
|
156
|
28
|
|
|
|
|
45
|
while (1) |
|
157
|
|
|
|
|
|
|
{ |
|
158
|
28
|
100
|
|
|
|
128
|
if ($self->_check_opass($opassword, $upassword)) |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
{ |
|
160
|
16
|
|
|
|
|
65
|
$self->{code} = $self->_compute_hash($doc->{ID}, $opassword); |
|
161
|
16
|
|
|
|
|
44
|
last; |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
elsif ($self->_check_upass($doc->{ID}, $upassword)) |
|
164
|
|
|
|
|
|
|
{ |
|
165
|
0
|
|
|
|
|
0
|
$self->{code} = $self->_compute_hash($doc->{ID}, $upassword); |
|
166
|
0
|
|
|
|
|
0
|
last; |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
elsif ($prompt) |
|
169
|
|
|
|
|
|
|
{ |
|
170
|
0
|
|
|
|
|
0
|
print {*STDERR} 'Enter owner password: '; |
|
|
0
|
|
|
|
|
0
|
|
|
171
|
0
|
|
|
|
|
0
|
$opassword = ; ## no critic(InputOutput::ProhibitExplicitStdin) |
|
172
|
0
|
|
|
|
|
0
|
chomp $opassword; |
|
173
|
|
|
|
|
|
|
|
|
174
|
0
|
|
|
|
|
0
|
print {*STDERR} 'Enter user password: '; |
|
|
0
|
|
|
|
|
0
|
|
|
175
|
0
|
|
|
|
|
0
|
$upassword = ; ## no critic(InputOutput::ProhibitExplicitStdin) |
|
176
|
0
|
|
|
|
|
0
|
chomp $upassword; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
else |
|
179
|
|
|
|
|
|
|
{ |
|
180
|
12
|
|
|
|
|
28
|
$CAM::PDF::errstr = "Incorrect password(s). The document cannot be decrypted.\n"; |
|
181
|
12
|
|
|
|
|
102
|
return; |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
|
|
185
|
16
|
|
|
|
|
43
|
$self->{opass} = $opassword; |
|
186
|
16
|
|
|
|
|
32
|
$self->{upass} = $upassword; |
|
187
|
|
|
|
|
|
|
|
|
188
|
16
|
|
|
|
|
161
|
return $self; |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item $self->decode_permissions($field) |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
Given a binary encoded permissions string from a PDF document, return |
|
194
|
|
|
|
|
|
|
the four individual boolean fields as an array: |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
print boolean |
|
197
|
|
|
|
|
|
|
modify boolean |
|
198
|
|
|
|
|
|
|
copy boolean |
|
199
|
|
|
|
|
|
|
add boolean |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=cut |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub decode_permissions |
|
204
|
|
|
|
|
|
|
{ |
|
205
|
28
|
|
|
28
|
1
|
41
|
my $self = shift; |
|
206
|
28
|
|
|
|
|
43
|
my $p = shift; |
|
207
|
|
|
|
|
|
|
|
|
208
|
28
|
|
|
|
|
127
|
my $bytes = unpack 'b*', pack 'V', $p; |
|
209
|
28
|
|
|
|
|
217
|
return split //xms, substr $bytes, 2, 4; |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=item $self->encode_permissions($print, $modify, $copy, $add) |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Given four booleans, pack them into a single field in the PDF style |
|
215
|
|
|
|
|
|
|
that decode_permissions can understand. Returns that scalar. |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=cut |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub encode_permissions |
|
220
|
|
|
|
|
|
|
{ |
|
221
|
12
|
|
|
12
|
1
|
27
|
my $self = shift; |
|
222
|
|
|
|
|
|
|
|
|
223
|
12
|
|
|
|
|
23
|
my %allow; |
|
224
|
12
|
|
|
|
|
29
|
$allow{print} = shift; |
|
225
|
12
|
|
|
|
|
33
|
$allow{modify} = shift; |
|
226
|
12
|
|
|
|
|
27
|
$allow{copy} = shift; |
|
227
|
12
|
|
|
|
|
29
|
$allow{add} = shift; |
|
228
|
|
|
|
|
|
|
|
|
229
|
12
|
|
|
|
|
53
|
foreach my $key (keys %allow) |
|
230
|
|
|
|
|
|
|
{ |
|
231
|
48
|
100
|
|
|
|
123
|
$allow{$key} = $allow{$key} ? 1 : 0; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# This is more complicated that decode, because we need to pad |
|
235
|
|
|
|
|
|
|
# endian-appropriately |
|
236
|
|
|
|
|
|
|
|
|
237
|
12
|
|
|
|
|
70
|
my $perms = join q{}, $allow{print}, $allow{modify}, $allow{copy}, $allow{add}; |
|
238
|
12
|
|
|
|
|
31
|
my $bytes = '00' . $perms . '11'; # 8 bits: 2 pad, 4 data, 2 pad |
|
239
|
|
|
|
|
|
|
# Pad to 32 bits with the right endian-ness |
|
240
|
12
|
|
|
|
|
56
|
my $binary = unpack 'B16', pack 's', 255; |
|
241
|
12
|
50
|
|
|
|
49
|
if ('1' eq substr $binary, 0, 1) |
|
242
|
|
|
|
|
|
|
{ |
|
243
|
|
|
|
|
|
|
# little endian |
|
244
|
12
|
|
|
|
|
28
|
$bytes .= '11111111' x 3; |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
else |
|
247
|
|
|
|
|
|
|
{ |
|
248
|
|
|
|
|
|
|
# big endian |
|
249
|
0
|
|
|
|
|
0
|
$bytes = ('11111111' x 3) . $bytes; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
# Make a signed 32-bit number (NOTE: should this really be signed??? need to check spec...) |
|
252
|
12
|
|
|
|
|
63
|
my $p = unpack 'l', pack 'b32', $bytes; |
|
253
|
|
|
|
|
|
|
|
|
254
|
12
|
|
|
|
|
54
|
return $p; |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=item $self->set_passwords($doc, $ownerpass, $userpass) |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=item $self->set_passwords($doc, $ownerpass, $userpass, $permissions) |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Change the PDF passwords to the specified values. When the PDF is |
|
262
|
|
|
|
|
|
|
output, it will be encrypted with the new passwords. |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
PERMISSIONS is an optional scalar of the form that decode_permissions |
|
265
|
|
|
|
|
|
|
can understand. If not specified, the existing values will be |
|
266
|
|
|
|
|
|
|
retained. |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
Note: we only support writing using encryption version 1, even though |
|
269
|
|
|
|
|
|
|
we can read encryption version 2 as well. |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=cut |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub set_passwords |
|
274
|
|
|
|
|
|
|
{ |
|
275
|
12
|
|
|
12
|
1
|
24
|
my $self = shift; |
|
276
|
12
|
|
|
|
|
24
|
my $doc = shift; |
|
277
|
12
|
|
|
|
|
21
|
my $opass = shift; |
|
278
|
12
|
|
|
|
|
24
|
my $upass = shift; |
|
279
|
12
|
|
33
|
|
|
60
|
my $p = shift || $self->{P} || $self->encode_permissions(1,1,1,1); |
|
280
|
|
|
|
|
|
|
|
|
281
|
12
|
|
|
|
|
98
|
require Digest::MD5; |
|
282
|
12
|
|
|
|
|
971
|
require Crypt::RC4; |
|
283
|
|
|
|
|
|
|
|
|
284
|
12
|
|
|
|
|
842
|
$doc->clean(); # Mark EVERYTHING changed |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# if no crypt block, create it and a trailer entry |
|
287
|
12
|
|
|
|
|
57
|
my $dict = CAM::PDF::Node->new('dictionary', |
|
288
|
|
|
|
|
|
|
{ |
|
289
|
|
|
|
|
|
|
Filter => CAM::PDF::Node->new('label', 'Standard'), |
|
290
|
|
|
|
|
|
|
V => CAM::PDF::Node->new('number', 1), |
|
291
|
|
|
|
|
|
|
R => CAM::PDF::Node->new('number', 2), |
|
292
|
|
|
|
|
|
|
P => CAM::PDF::Node->new('number', $p), |
|
293
|
|
|
|
|
|
|
O => CAM::PDF::Node->new('string', q{}), |
|
294
|
|
|
|
|
|
|
U => CAM::PDF::Node->new('string', q{}), |
|
295
|
|
|
|
|
|
|
}); |
|
296
|
12
|
|
|
|
|
49
|
my $objnode = CAM::PDF::Node->new('object', $dict); |
|
297
|
|
|
|
|
|
|
|
|
298
|
12
|
|
|
|
|
37
|
my $objnum = $self->{EncryptBlock}; |
|
299
|
12
|
100
|
|
|
|
29
|
if ($objnum) |
|
300
|
|
|
|
|
|
|
{ |
|
301
|
8
|
|
|
|
|
46
|
$doc->replaceObject($objnum, undef, $objnode, 0); |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
else |
|
304
|
|
|
|
|
|
|
{ |
|
305
|
4
|
|
|
|
|
23
|
$objnum = $doc->appendObject(undef, $objnode, 0); |
|
306
|
|
|
|
|
|
|
} |
|
307
|
|
|
|
|
|
|
|
|
308
|
12
|
50
|
|
|
|
47
|
if (!$doc->{trailer}) |
|
309
|
|
|
|
|
|
|
{ |
|
310
|
0
|
|
|
|
|
0
|
die 'No trailer'; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
# This may overwrite an existing ref, but that's no big deal, just a tiny bit inefficient |
|
314
|
12
|
|
|
|
|
55
|
$doc->{trailer}->{Encrypt} = CAM::PDF::Node->new('reference', $objnum); |
|
315
|
|
|
|
|
|
|
# if no ID, create it |
|
316
|
12
|
50
|
|
|
|
46
|
if (!$doc->{ID}) |
|
317
|
|
|
|
|
|
|
{ |
|
318
|
0
|
|
|
|
|
0
|
$doc->createID(); |
|
319
|
|
|
|
|
|
|
#print 'new ID: ' . unpack('h*',$doc->{ID}) . ' (' . length($doc->{ID}) . ")\n"; |
|
320
|
|
|
|
|
|
|
} |
|
321
|
|
|
|
|
|
|
#else { print 'old ID: '.unpack('h*',$doc->{ID}) . ' (' . length($doc->{ID}) . ")\n"; } |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
# Recompute O and U |
|
324
|
|
|
|
|
|
|
# To do so, we must set up a couple of dependent variables first: |
|
325
|
12
|
|
|
|
|
29
|
$self->{R} = 2; |
|
326
|
12
|
|
|
|
|
24
|
$self->{keylength} = 40; |
|
327
|
12
|
|
|
|
|
21
|
$self->{P} = $p; |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# set O (has to be first because U uses O) |
|
330
|
12
|
|
|
|
|
47
|
$self->{O} = $self->_compute_o($opass, $upass); |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# set U |
|
333
|
12
|
|
|
|
|
54
|
$self->{U} = $self->_compute_u($doc->{ID}, $upass); |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# save O and U in the Encrypt block |
|
336
|
12
|
|
|
|
|
12220
|
$dict = $doc->getObjValue($objnum); |
|
337
|
12
|
|
|
|
|
57
|
$dict->{O}->{value} = $self->{O}; |
|
338
|
12
|
|
|
|
|
32
|
$dict->{U}->{value} = $self->{U}; |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# Create a brand new instance |
|
341
|
12
|
|
|
|
|
26
|
my $pkg = ref $self; |
|
342
|
12
|
|
50
|
|
|
55
|
$doc->{crypt} = $pkg->new($doc, $opass, $upass, 0) |
|
343
|
|
|
|
|
|
|
|| die "$CAM::PDF::errstr\n"; |
|
344
|
|
|
|
|
|
|
|
|
345
|
12
|
|
|
|
|
89
|
return $doc->{crypt}; |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=item $self->encrypt($doc, $string) |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
Encrypt the scalar using the passwords previously specified. |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=cut |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
sub encrypt |
|
355
|
|
|
|
|
|
|
{ |
|
356
|
431
|
|
|
431
|
1
|
1582
|
my ($self, @rest) = @_; |
|
357
|
431
|
|
|
|
|
1196
|
return $self->_crypt(@rest); |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=item $self->decrypt($doc, $string) |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
Decrypt the scalar using the passwords previously specified. |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=cut |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
sub decrypt |
|
367
|
|
|
|
|
|
|
{ |
|
368
|
87
|
|
|
87
|
1
|
277
|
my ($self, @rest) = @_; |
|
369
|
87
|
|
|
|
|
242
|
return $self->_crypt(@rest); |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
# INTERNAL FUNCTION |
|
373
|
|
|
|
|
|
|
# The real work behind encrpyt/decrypt |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub _crypt |
|
376
|
|
|
|
|
|
|
{ |
|
377
|
518
|
|
|
518
|
|
733
|
my $self = shift; |
|
378
|
518
|
|
|
|
|
676
|
my $doc = shift; |
|
379
|
518
|
|
|
|
|
675
|
my $content = shift; |
|
380
|
518
|
|
|
|
|
707
|
my $objnum = shift; |
|
381
|
518
|
|
|
|
|
640
|
my $gennum = shift; |
|
382
|
|
|
|
|
|
|
|
|
383
|
518
|
100
|
|
|
|
3239
|
return $content if ($self->{noop}); |
|
384
|
|
|
|
|
|
|
|
|
385
|
85
|
50
|
33
|
|
|
685
|
if (ref $content || ref $objnum || ref $gennum) |
|
|
|
|
33
|
|
|
|
|
|
386
|
|
|
|
|
|
|
{ |
|
387
|
0
|
|
|
|
|
0
|
die 'Trying to crypt data with non-scalar obj/gennum or content'; |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# DO NOT encrypt the encryption block!! :-) |
|
391
|
85
|
100
|
66
|
|
|
671
|
return $content if ($objnum && $self->{EncryptBlock} && $objnum == $self->{EncryptBlock}); |
|
|
|
|
100
|
|
|
|
|
|
392
|
|
|
|
|
|
|
|
|
393
|
77
|
100
|
|
|
|
218
|
if (!defined $gennum) |
|
394
|
|
|
|
|
|
|
{ |
|
395
|
8
|
50
|
|
|
|
21
|
if (!$objnum) |
|
396
|
|
|
|
|
|
|
{ |
|
397
|
|
|
|
|
|
|
# This is not a real document object. It might be a trailer object. |
|
398
|
8
|
|
|
|
|
70
|
return $content; |
|
399
|
|
|
|
|
|
|
} |
|
400
|
|
|
|
|
|
|
|
|
401
|
0
|
|
|
|
|
0
|
croak 'gennum missing in crypt'; |
|
402
|
|
|
|
|
|
|
} |
|
403
|
|
|
|
|
|
|
|
|
404
|
69
|
|
|
|
|
204
|
return Crypt::RC4::RC4($self->_compute_key($objnum, $gennum), $content); |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
sub _compute_key |
|
408
|
|
|
|
|
|
|
{ |
|
409
|
69
|
|
|
69
|
|
98
|
my $self = shift; |
|
410
|
69
|
|
|
|
|
105
|
my $objnum = shift; |
|
411
|
69
|
|
|
|
|
111
|
my $gennum = shift; |
|
412
|
|
|
|
|
|
|
|
|
413
|
69
|
|
|
|
|
150
|
my $id = $objnum . '_' .$gennum; |
|
414
|
69
|
100
|
|
|
|
239
|
if (!exists $self->{keycache}->{$id}) |
|
415
|
|
|
|
|
|
|
{ |
|
416
|
44
|
|
|
|
|
169
|
my $objstr = pack 'V', $objnum; |
|
417
|
44
|
|
|
|
|
105
|
my $genstr = pack 'V', $gennum; |
|
418
|
|
|
|
|
|
|
|
|
419
|
44
|
|
|
|
|
103
|
my $objpadding = substr $objstr, 0, 3; |
|
420
|
44
|
|
|
|
|
81
|
my $genpadding = substr $genstr, 0, 2; |
|
421
|
|
|
|
|
|
|
|
|
422
|
44
|
|
|
|
|
418
|
my $hash = Digest::MD5::md5($self->{code} . $objpadding . $genpadding); |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# size(bytes) = nbits/8 + 3 for objnum + 2 for gennum; max of 16; PDF ref 1.5 pp 94-95 |
|
425
|
44
|
|
|
|
|
139
|
my $size = ($self->{keylength} >> 3) + 5; |
|
426
|
44
|
50
|
|
|
|
124
|
if ($size > 16) { |
|
427
|
0
|
|
|
|
|
0
|
$size = 16; |
|
428
|
|
|
|
|
|
|
} |
|
429
|
44
|
|
|
|
|
3953
|
$self->{keycache}->{$id} = substr $hash, 0, $size; |
|
430
|
|
|
|
|
|
|
} |
|
431
|
69
|
|
|
|
|
374
|
return $self->{keycache}->{$id}; |
|
432
|
|
|
|
|
|
|
} |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub _compute_hash |
|
435
|
|
|
|
|
|
|
{ |
|
436
|
40
|
|
|
40
|
|
76
|
my $self = shift; |
|
437
|
40
|
|
|
|
|
81
|
my $doc_id = shift; |
|
438
|
40
|
|
|
|
|
65
|
my $pass = shift; |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
#print "_compute_hash for password $pass, P: $self->{P}, ID: $doc_id, O: $self->{O}\n" if ($pass); |
|
441
|
|
|
|
|
|
|
|
|
442
|
40
|
|
|
|
|
106
|
$pass = $self->_format_pass($pass); |
|
443
|
|
|
|
|
|
|
|
|
444
|
40
|
|
|
|
|
154
|
my $p = pack 'L', $self->{P}+0; |
|
445
|
40
|
|
|
|
|
145
|
my $bytes = unpack 'b32', $p; |
|
446
|
40
|
50
|
|
|
|
154
|
if (1 == substr $bytes, 0, 1) |
|
447
|
|
|
|
|
|
|
{ |
|
448
|
|
|
|
|
|
|
# big endian, so byte swap |
|
449
|
0
|
|
|
|
|
0
|
$p = (substr $p,3,1).(substr $p,2,1).(substr $p,1,1).(substr $p,0,1); |
|
450
|
|
|
|
|
|
|
} |
|
451
|
|
|
|
|
|
|
|
|
452
|
40
|
|
|
|
|
79
|
my $id = substr $doc_id, 0, 16; |
|
453
|
|
|
|
|
|
|
|
|
454
|
40
|
|
|
|
|
105
|
my $input = $pass . $self->{O} . $p . $id; |
|
455
|
|
|
|
|
|
|
|
|
456
|
40
|
50
|
|
|
|
112
|
if ($self->{R} == 3) { |
|
457
|
|
|
|
|
|
|
# I don't know how to decide this. Maybe not applicable with Standard filter? |
|
458
|
|
|
|
|
|
|
#if document metadata is not encrypted |
|
459
|
|
|
|
|
|
|
# input .= pack 'L', 0xFFFFFFFF; |
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
|
|
462
|
40
|
|
|
|
|
182
|
my $hash = Digest::MD5::md5($input); |
|
463
|
|
|
|
|
|
|
|
|
464
|
40
|
50
|
|
|
|
121
|
if ($self->{R} == 3) |
|
465
|
|
|
|
|
|
|
{ |
|
466
|
0
|
|
|
|
|
0
|
for my $iter (1..50) { |
|
467
|
0
|
|
|
|
|
0
|
$hash = Digest::MD5::md5($hash); |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
} |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
# desired number of bytes for the key |
|
472
|
|
|
|
|
|
|
# for V==1, size == 5 |
|
473
|
|
|
|
|
|
|
# for V==2, 5 < size < 16 |
|
474
|
40
|
|
|
|
|
80
|
my $size = $self->{keylength} >> 3; |
|
475
|
40
|
|
|
|
|
168
|
return substr $hash, 0, $size; |
|
476
|
|
|
|
|
|
|
} |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub _compute_u |
|
479
|
|
|
|
|
|
|
{ |
|
480
|
24
|
|
|
24
|
|
39
|
my $self = shift; |
|
481
|
24
|
|
|
|
|
42
|
my $doc_id = shift; |
|
482
|
24
|
|
|
|
|
40
|
my $upass = shift; |
|
483
|
|
|
|
|
|
|
|
|
484
|
24
|
|
|
|
|
134
|
my $hash = $self->_compute_hash($doc_id, $upass); |
|
485
|
24
|
50
|
|
|
|
59
|
if ($self->{R} == 3) { |
|
486
|
0
|
|
|
|
|
0
|
my $id = substr $doc_id, 0, 16; |
|
487
|
0
|
|
|
|
|
0
|
my $input = $padding . $id; |
|
488
|
0
|
|
|
|
|
0
|
my $code = Digest::MD5::md5($input); |
|
489
|
0
|
|
|
|
|
0
|
$code = substr $code, 0, 16; |
|
490
|
0
|
|
|
|
|
0
|
return $self->_do_iter_crypt($hash, $code) . substr $padding, 0, 16; |
|
491
|
|
|
|
|
|
|
} else { |
|
492
|
24
|
|
|
|
|
72
|
return Crypt::RC4::RC4($hash, $padding); |
|
493
|
|
|
|
|
|
|
} |
|
494
|
|
|
|
|
|
|
} |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
sub _compute_o |
|
497
|
|
|
|
|
|
|
{ |
|
498
|
40
|
|
|
40
|
|
66
|
my $self = shift; |
|
499
|
40
|
|
|
|
|
59
|
my $opass = shift; |
|
500
|
40
|
|
|
|
|
54
|
my $upass = shift; |
|
501
|
40
|
|
|
|
|
55
|
my $backward = shift; |
|
502
|
|
|
|
|
|
|
|
|
503
|
40
|
|
|
|
|
127
|
my $o = $self->_format_pass($opass); |
|
504
|
40
|
|
|
|
|
85
|
my $u = $self->_format_pass($upass); |
|
505
|
|
|
|
|
|
|
|
|
506
|
40
|
|
|
|
|
229
|
my $hash = Digest::MD5::md5($o); |
|
507
|
|
|
|
|
|
|
|
|
508
|
40
|
50
|
|
|
|
121
|
if ($self->{R} == 3) { |
|
509
|
0
|
|
|
|
|
0
|
for my $iter (1..50) { |
|
510
|
0
|
|
|
|
|
0
|
$hash = Digest::MD5::md5($hash); |
|
511
|
|
|
|
|
|
|
} |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
|
|
514
|
40
|
|
|
|
|
185
|
my $size = $self->{keylength} >> 3; |
|
515
|
40
|
|
|
|
|
72
|
my $code = substr $hash, 0, $size; |
|
516
|
40
|
|
|
|
|
136
|
return $self->_do_iter_crypt($code, $u, $backward); |
|
517
|
|
|
|
|
|
|
} |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
sub _do_iter_crypt { |
|
520
|
40
|
|
|
40
|
|
58
|
my $self = shift; |
|
521
|
40
|
|
|
|
|
57
|
my $code = shift; |
|
522
|
40
|
|
|
|
|
55
|
my $pass = shift; |
|
523
|
40
|
|
|
|
|
68
|
my $backward = shift; |
|
524
|
|
|
|
|
|
|
|
|
525
|
40
|
50
|
|
|
|
120
|
if ($self->{R} == 3) { |
|
526
|
0
|
|
|
|
|
0
|
my @steps = 0..19; |
|
527
|
0
|
0
|
|
|
|
0
|
if ($backward) { |
|
528
|
0
|
|
|
|
|
0
|
@steps = reverse @steps; |
|
529
|
|
|
|
|
|
|
} |
|
530
|
0
|
|
|
|
|
0
|
my $size = $self->{keylength} >> 3; |
|
531
|
0
|
|
|
|
|
0
|
for my $iter (@steps) { |
|
532
|
0
|
|
|
|
|
0
|
my $xor = chr($iter) x $size; |
|
533
|
0
|
|
|
|
|
0
|
my $itercode = $code ^ $xor; |
|
534
|
0
|
|
|
|
|
0
|
$pass = Crypt::RC4::RC4($itercode, $pass); |
|
535
|
|
|
|
|
|
|
} |
|
536
|
|
|
|
|
|
|
} else { |
|
537
|
40
|
|
|
|
|
185
|
$pass = Crypt::RC4::RC4($code, $pass); |
|
538
|
|
|
|
|
|
|
} |
|
539
|
40
|
|
|
|
|
27978
|
return $pass; |
|
540
|
|
|
|
|
|
|
} |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
sub _check_opass |
|
543
|
|
|
|
|
|
|
{ |
|
544
|
28
|
|
|
28
|
|
47
|
my $self = shift; |
|
545
|
28
|
|
|
|
|
47
|
my $opass = shift; |
|
546
|
28
|
|
|
|
|
40
|
my $upass = shift; |
|
547
|
|
|
|
|
|
|
|
|
548
|
28
|
|
|
|
|
94
|
my $crypto = $self->_compute_o($opass, $upass, 1); |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
#printf "O: %s\n%s\n vs.\n%s\n", defined $opass ? $opass : '(undef)', _hex($crypto), _hex($self->{O}); |
|
551
|
|
|
|
|
|
|
|
|
552
|
28
|
|
|
|
|
196
|
return $crypto eq $self->{O}; |
|
553
|
|
|
|
|
|
|
} |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
sub _check_upass |
|
556
|
|
|
|
|
|
|
{ |
|
557
|
12
|
|
|
12
|
|
24
|
my $self = shift; |
|
558
|
12
|
|
|
|
|
20
|
my $doc_id = shift; |
|
559
|
12
|
|
|
|
|
20
|
my $upass = shift; |
|
560
|
|
|
|
|
|
|
|
|
561
|
12
|
|
|
|
|
44
|
my $cryptu = $self->_compute_u($doc_id, $upass); |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
#printf "U: %s\n%s\n vs.\n%s\n", defined $upass ? $upass : '(undef)', _hex($cryptu), _hex($self->{U}); |
|
564
|
|
|
|
|
|
|
|
|
565
|
12
|
|
|
|
|
9455
|
return $cryptu eq $self->{U}; |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
sub _hex { |
|
569
|
0
|
|
|
0
|
|
0
|
my $val = shift; |
|
570
|
0
|
|
|
|
|
0
|
return join q{}, map {sprintf '%08x', $_} unpack 'N*', $val; |
|
|
0
|
|
|
|
|
0
|
|
|
571
|
|
|
|
|
|
|
} |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
sub _format_pass |
|
574
|
|
|
|
|
|
|
{ |
|
575
|
120
|
|
|
120
|
|
155
|
my $self = shift; |
|
576
|
120
|
|
|
|
|
166
|
my $pass = shift; |
|
577
|
|
|
|
|
|
|
|
|
578
|
120
|
100
|
|
|
|
297
|
if (!defined $pass) |
|
579
|
|
|
|
|
|
|
{ |
|
580
|
12
|
|
|
|
|
20
|
$pass = q{}; |
|
581
|
|
|
|
|
|
|
} |
|
582
|
|
|
|
|
|
|
|
|
583
|
120
|
|
|
|
|
332
|
return substr $pass.$padding, 0, 32; |
|
584
|
|
|
|
|
|
|
} |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
1; |
|
587
|
|
|
|
|
|
|
__END__ |