mirror of
https://github.com/qpdf/qpdf.git
synced 2024-11-11 07:30:57 +00:00
a2c4a2b12a
Now the tests intermix old and new syntax, ensuring both are exercised.
731 lines
32 KiB
Perl
731 lines
32 KiB
Perl
#!/usr/bin/env perl
|
|
require 5.008;
|
|
use warnings;
|
|
use strict;
|
|
|
|
unshift(@INC, '.');
|
|
require qpdf_test_helpers;
|
|
|
|
chdir("qpdf") or die "chdir testdir failed: $!\n";
|
|
|
|
require TestDriver;
|
|
|
|
cleanup();
|
|
|
|
my $td = new TestDriver('encryption');
|
|
|
|
my $n_tests = 0;
|
|
# $n_tests incremented below
|
|
|
|
# The enc-file.pdf files were encrypted using Acrobat 5.0, not the
|
|
# qpdf library. The files are decrypted using qpdf, then re-encrypted
|
|
# using qpdf with specific flags. The /P value is checked. The
|
|
# resulting files were saved and manually checked with Acrobat 5.0 to
|
|
# ensure that the security settings were as intended.
|
|
|
|
# The enc-XI-file.pdf files were treated the same way but with Acrobat
|
|
# XI instead of Acrobat 5.0. They were used to create test files with
|
|
# newer encryption formats.
|
|
|
|
# Values: basename, password, encryption flags, /P Encrypt key,
|
|
# extract-for-accessibility, extract-for-any-purpose,
|
|
# print-low-res, print-high-res, modify-assembly, modify-forms,
|
|
# modify-annotate, modify-other, modify-all
|
|
my @encrypted_files =
|
|
(['base', ''], # 1
|
|
['R3,V2', '', # 2
|
|
'-accessibility=n -extract=n -print=full -modify=all', -532,
|
|
1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1],
|
|
['R3,V2,U=view,O=view', 'view', # 3
|
|
'-accessibility=y -extract=n -print=none -modify=none', -3392,
|
|
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
['R3,V2,O=master', 'master', # 4
|
|
'-accessibility=n -extract=y -print=none -modify=annotate', -2576,
|
|
1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0],
|
|
['R3,V2,O=master', '', # 5
|
|
'-accessibility=n -extract=n -print=none -modify=form', -2624,
|
|
0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0],
|
|
['R3,V2,U=view,O=master', 'view', # 6
|
|
'-accessibility=n -extract=n -print=none -modify=assembly', -2880,
|
|
0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
['R3,V2,U=view,O=master', 'master', # 7
|
|
'-accessibility=n -print=low', -2564,
|
|
1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1],
|
|
['R3,V2,U=view,O=master', 'master', # 8
|
|
'-modify=all -assemble=n', -1028,
|
|
1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0],
|
|
['R3,V2,U=view,O=master', 'master', # 9
|
|
'-modify=none -form=y', -1068,
|
|
1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0],
|
|
['R3,V2,U=view,O=master', 'master', # 10
|
|
'-modify=annotate -assemble=n', -1036,
|
|
1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0],
|
|
['R3,V2,U=view,O=master', 'master', # 11
|
|
'-form=n', -260,
|
|
1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0],
|
|
['R3,V2,U=view,O=master', 'master', # 12
|
|
'-annotate=n', -36,
|
|
1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0],
|
|
['R3,V2,U=view,O=master', 'master', # 13
|
|
'-modify-other=n', -12,
|
|
1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
['R2,V1', '', # 14
|
|
'-print=n -modify=n -extract=n -annotate=n', -64,
|
|
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
['R2,V1,U=view,O=view', 'view', # 15
|
|
'-print=y -modify=n -extract=n -annotate=n', -60,
|
|
1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
['R2,V1,O=master', 'master', # 16
|
|
'-print=n -modify=y -extract=n -annotate=n', -56,
|
|
1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
|
|
['R2,V1,O=master', '', # 17
|
|
'-print=n -modify=n -extract=y -annotate=n', -48,
|
|
0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
['R2,V1,U=view,O=master', 'view', # 18
|
|
'-print=n -modify=n -extract=n -annotate=y', -32,
|
|
0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
|
|
['R2,V1,U=view,O=master', 'master', # 19
|
|
'', -4,
|
|
1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
['long-password', 'asdf asdf asdf asdf asdf asdf qwer'], # 20
|
|
['long-password', 'asdf asdf asdf asdf asdf asdf qw'], # 21
|
|
['XI-base', ''], # 22
|
|
['XI-R6,V5,O=master', '', # 23
|
|
'-extract=n -print=none -modify=assembly', -2368,
|
|
0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
['XI-R6,V5,O=master', 'master', # 24
|
|
'-extract=n -print=none -modify=assembly', -2368,
|
|
1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
['XI-R6,V5,U=view,O=master', 'view', # 25
|
|
'-print=low', -2052,
|
|
0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
|
|
['XI-R6,V5,U=view,O=master', 'master', # 26
|
|
'-print=low', -2052,
|
|
1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1],
|
|
['XI-R6,V5,U=view,O=master', 'master', # 27
|
|
'-accessibility=n', -4, # -accessibility=n has no effect
|
|
1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
['XI-long-password', 'qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnm'], # 28; -accessibility=n has no effect
|
|
['XI-long-password', 'qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcv'], # 29
|
|
['XI-R6,V5,U=wwwww,O=wwwww', 'wwwww', # 30
|
|
'', -4,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
);
|
|
|
|
$n_tests += 8 + (2 * (@encrypted_files)) + (7 * (@encrypted_files - 6)) + 10;
|
|
|
|
$td->runtest("encrypted file",
|
|
{$td->COMMAND => "test_driver 2 encrypted-with-images.pdf"},
|
|
{$td->FILE => "encrypted1.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("preserve encryption",
|
|
{$td->COMMAND => "qpdf encrypted-with-images.pdf encrypted-with-images.enc"},
|
|
{$td->STRING => "",
|
|
$td->EXIT_STATUS => 0});
|
|
$td->runtest("recheck encrypted file",
|
|
{$td->COMMAND => "test_driver 2 encrypted-with-images.enc"},
|
|
{$td->FILE => "encrypted1.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
$td->runtest("empty owner password",
|
|
{$td->COMMAND => "qpdf --encrypt u '' 256 -- minimal.pdf a.pdf"},
|
|
{$td->REGEXP => ".*is insecure.*--allow-insecure.*",
|
|
$td->EXIT_STATUS => 2},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("allow insecure",
|
|
{$td->COMMAND => "qpdf --encrypt --user-password=u --bits=256 --allow-insecure --" .
|
|
" minimal.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("check insecure",
|
|
{$td->COMMAND => "qpdf --check a.pdf"},
|
|
{$td->FILE => "insecure-passwords.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
# Test that long passwords that are one character too short fail. We
|
|
# test the truncation cases in the loop below by using passwords
|
|
# longer than the supported length.
|
|
$td->runtest("significant password characters (V < 5)",
|
|
{$td->COMMAND => "qpdf --check enc-long-password.pdf" .
|
|
" --password='asdf asdf asdf asdf asdf asdf q'"},
|
|
{$td->REGEXP => ".*invalid password.*", $td->EXIT_STATUS => 2});
|
|
$td->runtest("significant password characters (V = 5)",
|
|
{$td->COMMAND => "qpdf --check enc-XI-long-password.pdf" .
|
|
" --password=qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxc"},
|
|
{$td->REGEXP => ".*invalid password.*", $td->EXIT_STATUS => 2});
|
|
|
|
my $enc_base = undef;
|
|
my $enc_n = 0;
|
|
foreach my $d (@encrypted_files)
|
|
{
|
|
++$enc_n;
|
|
my ($file, $pass, $xeflags, $P, $match_owner, $match_user,
|
|
$accessible, $extract, $printlow, $printhigh,
|
|
$modifyassembly, $modifyform, $modifyannot,
|
|
$modifyother, $modifyall) = @$d;
|
|
|
|
my $f = sub { $_[0] ? "allowed" : "not allowed" };
|
|
my $jf = sub { $_[0] ? "true" : "false" };
|
|
my $enc_details = "";
|
|
my $enc_json =
|
|
"{\n" .
|
|
" \"version\": 2,\n" .
|
|
" \"parameters\": {\n" .
|
|
" \"decodelevel\": \"generalized\"\n" .
|
|
" },\n" .
|
|
" \"encrypt\": {\n" .
|
|
" \"capabilities\": {\n";
|
|
if ($match_owner)
|
|
{
|
|
$enc_details .= "Supplied password is owner password\n";
|
|
}
|
|
if ($match_user)
|
|
{
|
|
$enc_details .= "Supplied password is user password\n";
|
|
}
|
|
$enc_details .=
|
|
"extract for accessibility: " . &$f($accessible) . "\n" .
|
|
"extract for any purpose: " . &$f($extract) . "\n" .
|
|
"print low resolution: " . &$f($printlow) . "\n" .
|
|
"print high resolution: " . &$f($printhigh) . "\n" .
|
|
"modify document assembly: " . &$f($modifyassembly) . "\n" .
|
|
"modify forms: " . &$f($modifyform) . "\n" .
|
|
"modify annotations: " . &$f($modifyannot) . "\n" .
|
|
"modify other: " . &$f($modifyother) . "\n" .
|
|
"modify anything: " . &$f($modifyall) . "\n";
|
|
$enc_json .=
|
|
" \"accessibility\": " . &$jf($accessible) . ",\n" .
|
|
" \"extract\": " . &$jf($extract) . ",\n" .
|
|
" \"modify\": " . &$jf($modifyall) . ",\n" .
|
|
" \"modifyannotations\": " . &$jf($modifyannot) . ",\n" .
|
|
" \"modifyassembly\": " . &$jf($modifyassembly) . ",\n" .
|
|
" \"modifyforms\": " . &$jf($modifyform) . ",\n" .
|
|
" \"modifyother\": " . &$jf($modifyother) . ",\n" .
|
|
" \"printhigh\": " . &$jf($printhigh) . ",\n" .
|
|
" \"printlow\": " . &$jf($printlow) . "\n" .
|
|
" },\n" .
|
|
" \"encrypted\": true,\n" .
|
|
" \"ownerpasswordmatched\": ---opm---,\n" .
|
|
" \"parameters\": {\n" .
|
|
" \"P\": ---P---,\n" .
|
|
" \"R\": ---R---,\n" .
|
|
" \"V\": ---V---,\n" .
|
|
" \"bits\": ---bits---,\n" .
|
|
" \"filemethod\": \"---method---\",\n" .
|
|
" \"key\": null,\n" .
|
|
" \"method\": \"---method---\",\n" .
|
|
" \"streammethod\": \"---method---\",\n" .
|
|
" \"stringmethod\": \"---method---\"\n" .
|
|
" },\n" .
|
|
" \"recovereduserpassword\": ---rup---,\n" .
|
|
" \"userpasswordmatched\": ---upm---\n" .
|
|
" }\n" .
|
|
"}\n";
|
|
if ($file =~ m/XI-/)
|
|
{
|
|
$enc_details .=
|
|
"stream encryption method: AESv3\n" .
|
|
"string encryption method: AESv3\n" .
|
|
"file encryption method: AESv3\n";
|
|
}
|
|
|
|
# Test writing to stdout
|
|
$td->runtest("decrypt $file",
|
|
{$td->COMMAND =>
|
|
"qpdf --static-id -qdf --object-streams=disable" .
|
|
" --no-original-object-ids" .
|
|
" --password=\"$pass\" enc-$file.pdf -" .
|
|
" > $file.enc"},
|
|
{$td->STRING => "",
|
|
$td->EXIT_STATUS => 0});
|
|
if ($file =~ m/base$/)
|
|
{
|
|
$enc_base = $file;
|
|
$td->runtest("check ID",
|
|
{$td->COMMAND => "perl check-ID.pl $file.enc"},
|
|
{$td->STRING => "ID okay\n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
}
|
|
else
|
|
{
|
|
$td->runtest("check against base",
|
|
{$td->COMMAND =>
|
|
"sh ./diff-encrypted $enc_base.enc $file.enc"},
|
|
{$td->STRING => "okay\n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
}
|
|
if ($file =~ m/^(?:XI-)?R(\d),V(\d)(?:,U=(\w+))?(?:,O=(\w+))?$/)
|
|
{
|
|
my $R = $1;
|
|
my $V = $2;
|
|
my $upass = $3 || "";
|
|
my $opass = $4 || "";
|
|
my $bits = (($V == 5) ? 256 : ($V == 2) ? 128 : 40);
|
|
my $method = $bits == 256 ? "AESv3" : "RC4";
|
|
my $opm = ($pass eq $opass ? "true" : "false");
|
|
my $upm = ($pass eq $upass ? "true" : "false");
|
|
my $rup = (($pass eq $opass) && ($pass ne $upass) && ($V < 5))
|
|
? "\"$upass\"" : "null";
|
|
$enc_json =~ s/---R---/$R/;
|
|
$enc_json =~ s/---P---/$P/;
|
|
$enc_json =~ s/---V---/$V/;
|
|
$enc_json =~ s/---bits---/$bits/;
|
|
$enc_json =~ s/---method---/$method/g;
|
|
$enc_json =~ s/---opm---/$opm/;
|
|
$enc_json =~ s/---upm---/$upm/;
|
|
$enc_json =~ s/---rup---/$rup/;
|
|
|
|
my $eflags = "--allow-weak-crypto" .
|
|
" -encrypt \"$upass\" \"$opass\" $bits $xeflags --";
|
|
if (($opass eq "") && ($bits == 256))
|
|
{
|
|
$eflags =~ s/--$/--allow-insecure --/;
|
|
}
|
|
if (($pass ne $upass) && ($V >= 5))
|
|
{
|
|
# V >= 5 can no longer recover user password with owner
|
|
# password.
|
|
$upass = "";
|
|
}
|
|
my $accessibility_warning = "";
|
|
if (($R > 3) && ($eflags =~ /accessibility=n/))
|
|
{
|
|
$accessibility_warning =
|
|
"qpdf: -accessibility=n is ignored" .
|
|
" for modern encryption formats\n";
|
|
}
|
|
$td->runtest("encrypt $file",
|
|
{$td->COMMAND =>
|
|
"qpdf --static-id --no-original-object-ids -qdf" .
|
|
" $eflags $file.enc $file.enc2"},
|
|
{$td->STRING => $accessibility_warning,
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("check /P enc2 ($enc_n)",
|
|
{$td->COMMAND =>
|
|
"qpdf --show-encryption --password=\"$pass\"" .
|
|
" $file.enc2"},
|
|
{$td->STRING => "R = $R\nP = $P\n" .
|
|
"User password = $upass\n$enc_details",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("json encrypt key ($enc_n)",
|
|
{$td->COMMAND =>
|
|
"qpdf --json --json-key=encrypt" .
|
|
" --password=\"$pass\"" .
|
|
" $file.enc2"},
|
|
{$td->STRING => $enc_json, $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("decrypt again",
|
|
{$td->COMMAND =>
|
|
"qpdf --static-id --no-original-object-ids -qdf" .
|
|
" --password=\"$pass\"" .
|
|
" $file.enc2 $file.enc3"},
|
|
{$td->STRING => "",
|
|
$td->EXIT_STATUS => 0});
|
|
$td->runtest("compare",
|
|
{$td->FILE => "$file.enc"},
|
|
{$td->FILE => "$file.enc3"});
|
|
$td->runtest("preserve encryption",
|
|
{$td->COMMAND =>
|
|
"qpdf --static-id --password=\"$pass\"" .
|
|
" $file.enc2 $file.enc4"},
|
|
{$td->STRING => "",
|
|
$td->EXIT_STATUS => 0});
|
|
$td->runtest("check /P enc4 ($enc_n)",
|
|
{$td->COMMAND =>
|
|
"qpdf --show-encryption --password=\"$pass\"" .
|
|
" $file.enc4"},
|
|
{$td->STRING => "R = $R\nP = $P\n" .
|
|
"User password = $upass\n$enc_details",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
}
|
|
}
|
|
|
|
$td->runtest("non-encrypted",
|
|
{$td->COMMAND => "qpdf --show-encryption enc-base.pdf"},
|
|
{$td->STRING => "File is not encrypted\n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
$td->runtest("invalid password",
|
|
{$td->COMMAND => "qpdf -qdf --password=quack" .
|
|
" enc-R2,V1,U=view,O=view.pdf a.qdf"},
|
|
{$td->STRING =>
|
|
"qpdf: enc-R2,V1,U=view,O=view.pdf: invalid password\n",
|
|
$td->EXIT_STATUS => 2},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("C API: invalid password",
|
|
{$td->COMMAND =>
|
|
"qpdf-ctest 2 enc-R2,V1,U=view,O=view.pdf '' a.qdf"},
|
|
{$td->FILE => "c-invalid-password.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("show-encryption works invalid password",
|
|
{$td->COMMAND => "qpdf --show-encryption --password=quack" .
|
|
" enc-R2,V1,U=view,O=view.pdf"},
|
|
{$td->FILE => "invalid-password-encrypt.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
my @cenc = (
|
|
[11, 'hybrid-xref.pdf', "''", 'r2', "", "user1"],
|
|
[12, 'hybrid-xref.pdf', "''", 'r3', "", "user2"],
|
|
[15, 'hybrid-xref.pdf', "''", 'r4', "", "user2"],
|
|
[17, 'hybrid-xref.pdf', "''", 'r5', "", "owner3"],
|
|
[18, 'hybrid-xref.pdf', "''", 'r6', "", "user4"],
|
|
[13, 'c-r2.pdf', 'user1', 'decrypt with user',
|
|
"user password: user1\n", ""],
|
|
[13, 'c-r3.pdf', 'owner2', 'decrypt with owner',
|
|
"user password: user2\n", ""],
|
|
[13, 'c-r5-in.pdf', 'user3', 'decrypt R5 with user',
|
|
"user password: user3\n", ""],
|
|
[13, 'c-r6-in.pdf', 'owner4', 'decrypt R6 with owner',
|
|
"user password: \n", ""],
|
|
);
|
|
$n_tests += 2 * @cenc;
|
|
|
|
foreach my $d (@cenc)
|
|
{
|
|
my ($n, $infile, $pass, $description, $output, $checkpass) = @$d;
|
|
my $outfile = $description;
|
|
$outfile =~ s/ /-/g;
|
|
my $pdf_outfile = "c-$outfile.pdf";
|
|
my $check_outfile = "c-$outfile.out";
|
|
$td->runtest("C API encryption: $description",
|
|
{$td->COMMAND => "qpdf-ctest $n $infile $pass a.pdf"},
|
|
{$td->STRING => $output . "C test $n done\n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
if (-f $pdf_outfile)
|
|
{
|
|
$td->runtest("check $description content",
|
|
{$td->COMMAND => "qpdf-test-compare a.pdf $pdf_outfile $checkpass"},
|
|
{$td->FILE => $pdf_outfile, $td->EXIT_STATUS => 0});
|
|
}
|
|
else
|
|
{
|
|
# QPDF doesn't provide any way to make the random bits in
|
|
# /Perms static, so we have no way to predictably create a
|
|
# /V=5 encrypted file. It's not worth adding this...the test
|
|
# suite is adequate without having a statically predictable
|
|
# file. (qpdf-test-compare ignores /Perms, but it's not worth
|
|
# adding output files for these cases.)
|
|
$td->runtest("check $description",
|
|
{$td->COMMAND =>
|
|
"qpdf --check a.pdf --password=$checkpass"},
|
|
{$td->FILE => $check_outfile, $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
}
|
|
}
|
|
|
|
# Test combinations of linearization and encryption. Note that we do
|
|
# content checks on encrypted and linearized files in various
|
|
# combinations below. Here we are just making sure that they are
|
|
# linearized and/or encrypted as desired.
|
|
|
|
$td->runtest("linearize encrypted file",
|
|
{$td->COMMAND => "qpdf --linearize encrypted-with-images.pdf a.pdf"},
|
|
{$td->STRING => "",
|
|
$td->EXIT_STATUS => 0});
|
|
$td->runtest("check encryption",
|
|
{$td->COMMAND => "qpdf --show-encryption a.pdf",
|
|
$td->FILTER => "grep -v allowed | grep -v Supplied"},
|
|
{$td->STRING => "R = 3\nP = -4\nUser password = \n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("check linearization",
|
|
{$td->COMMAND => "qpdf --check-linearization a.pdf"},
|
|
{$td->STRING => "a.pdf: no linearization errors\n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("linearize and encrypt file",
|
|
{$td->COMMAND =>
|
|
"qpdf --linearize --encrypt user owner 128 --use-aes=y --" .
|
|
" lin-special.pdf a.pdf"},
|
|
{$td->STRING => "",
|
|
$td->EXIT_STATUS => 0});
|
|
$td->runtest("check encryption",
|
|
{$td->COMMAND => "qpdf --show-encryption --password=owner a.pdf",
|
|
$td->FILTER => "grep -v allowed | grep -v method | grep -v Supplied"},
|
|
{$td->STRING => "R = 4\nP = -4\nUser password = user\n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("check linearization",
|
|
{$td->COMMAND => "qpdf --check-linearization" .
|
|
" --password=user a.pdf"},
|
|
{$td->STRING => "a.pdf: no linearization errors\n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
# Test --check-linearization of non-linearized file
|
|
$n_tests += 1;
|
|
$td->runtest("check linearization of non-linearized file",
|
|
{$td->COMMAND => "qpdf --check-linearization minimal.pdf"},
|
|
{$td->STRING => "minimal.pdf is not linearized\n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
|
|
# Test AES encryption in various ways.
|
|
$n_tests += 18;
|
|
$td->runtest("encrypt with AES",
|
|
{$td->COMMAND => "qpdf --encrypt '' o 128 --use-aes=y --" .
|
|
" enc-base.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("check encryption",
|
|
{$td->COMMAND => "qpdf --show-encryption a.pdf",
|
|
$td->FILTER => "grep -v allowed | grep -v method | grep -v Supplied"},
|
|
{$td->STRING => "R = 4\nP = -4\nUser password = \n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("convert original to qdf",
|
|
{$td->COMMAND => "qpdf --static-id --no-original-object-ids" .
|
|
" --qdf --min-version=1.6 enc-base.pdf a.qdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("convert encrypted to qdf",
|
|
{$td->COMMAND => "qpdf --static-id --no-original-object-ids" .
|
|
" --qdf a.pdf b.qdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("compare files",
|
|
{$td->COMMAND => "qpdf-test-compare a.qdf b.qdf"},
|
|
{$td->FILE => 'b.qdf', $td->EXIT_STATUS => 0});
|
|
$td->runtest("linearize with AES and object streams",
|
|
{$td->COMMAND => "qpdf --encrypt '' o 128 --use-aes=y --" .
|
|
" --linearize --object-streams=generate enc-base.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("check encryption",
|
|
{$td->COMMAND => "qpdf --show-encryption a.pdf",
|
|
$td->FILTER => "grep -v allowed | grep -v method | grep -v Supplied"},
|
|
{$td->STRING => "R = 4\nP = -4\nUser password = \n",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("linearize original",
|
|
{$td->COMMAND => "qpdf --linearize --object-streams=generate" .
|
|
" enc-base.pdf b.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("convert linearized original to qdf",
|
|
{$td->COMMAND => "qpdf --static-id --no-original-object-ids" .
|
|
" --qdf --object-streams=generate --min-version=1.6" .
|
|
" b.pdf a.qdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("convert encrypted to qdf",
|
|
{$td->COMMAND => "qpdf --static-id --no-original-object-ids" .
|
|
" --qdf --object-streams=generate a.pdf b.qdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("compare files",
|
|
{$td->FILE => 'a.qdf'},
|
|
{$td->FILE => 'b.qdf'});
|
|
$td->runtest("force version on aes encrypted",
|
|
{$td->COMMAND => "qpdf --force-version=1.4 a.pdf b.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("check",
|
|
{$td->COMMAND => "qpdf --check b.pdf"},
|
|
{$td->FILE => "aes-forced-check.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("make sure there is no xref stream",
|
|
{$td->COMMAND => "grep /ObjStm b.pdf | wc -l"},
|
|
{$td->REGEXP => "\\s*0\\s*", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("encrypt with V=5,R=5",
|
|
{$td->COMMAND =>
|
|
"qpdf --encrypt user owner 256 --force-R5 -- " .
|
|
"minimal.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("check encryption",
|
|
{$td->COMMAND => "qpdf --check a.pdf --password=owner"},
|
|
{$td->FILE => "V5R5.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("encrypt with V=5,R=6",
|
|
{$td->COMMAND =>
|
|
"qpdf --encrypt user owner 256 -- " .
|
|
"minimal.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("check encryption",
|
|
{$td->COMMAND => "qpdf --check a.pdf --password=user"},
|
|
{$td->FILE => "V5R6.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
# Look at some actual V4 files
|
|
$n_tests += 17;
|
|
foreach my $d (['--force-V4', 'V4'],
|
|
['--cleartext-metadata', 'V4-clearmeta'],
|
|
['--use-aes=y', 'V4-aes'],
|
|
['--cleartext-metadata --use-aes=y', 'V4-aes-clearmeta'])
|
|
{
|
|
my ($args, $out) = @$d;
|
|
$td->runtest("encrypt $args",
|
|
{$td->COMMAND => "qpdf --static-aes-iv --static-id" .
|
|
" --allow-weak-crypto --encrypt --bits=128 $args --" .
|
|
" enc-base.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("check output",
|
|
{$td->COMMAND => "qpdf-test-compare a.pdf $out.pdf"},
|
|
{$td->FILE => "$out.pdf", $td->EXIT_STATUS => 0});
|
|
$td->runtest("show encryption",
|
|
{$td->COMMAND => "qpdf --show-encryption a.pdf"},
|
|
{$td->FILE => "$out-encryption.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
}
|
|
# Crypt Filter
|
|
$td->runtest("decrypt with crypt filter",
|
|
{$td->COMMAND => "qpdf --decrypt --static-id" .
|
|
" metadata-crypt-filter.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("check output",
|
|
{$td->FILE => 'a.pdf'},
|
|
{$td->FILE => 'decrypted-crypt-filter.pdf'});
|
|
$td->runtest("nontrivial crypt filter",
|
|
{$td->COMMAND => "qpdf --qdf --decrypt --static-id" .
|
|
" nontrivial-crypt-filter.pdf --password=asdfqwer a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("check output",
|
|
{$td->FILE => 'a.pdf'},
|
|
{$td->FILE => 'nontrivial-crypt-filter-decrypted.pdf'});
|
|
$td->runtest("show nontrivial EFF",
|
|
{$td->COMMAND => "qpdf --show-encryption" .
|
|
" nontrivial-crypt-filter.pdf --password=asdfqwer"},
|
|
{$td->FILE => "nontrivial-crypt-filter.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
# Copy encryption parameters
|
|
$n_tests += 10;
|
|
$td->runtest("create reference qdf",
|
|
{$td->COMMAND =>
|
|
"qpdf --qdf --no-original-object-ids minimal.pdf a.qdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("create encrypted file",
|
|
{$td->COMMAND =>
|
|
"qpdf --encrypt user owner 128 --use-aes=y --extract=n --" .
|
|
" minimal.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("copy encryption parameters",
|
|
{$td->COMMAND => "test_driver 30 minimal.pdf a.pdf"},
|
|
{$td->STRING => "test 30 done\n", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("check output encryption",
|
|
{$td->COMMAND => "qpdf --show-encryption b.pdf --password=owner"},
|
|
{$td->FILE => "copied-encryption.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("convert to qdf",
|
|
{$td->COMMAND =>
|
|
"qpdf --qdf b.pdf b.qdf" .
|
|
" --password=owner --no-original-object-ids"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("compare qdf",
|
|
{$td->COMMAND => "sh ./diff-ignore-ID-version a.qdf b.qdf"},
|
|
{$td->STRING => "okay\n", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("copy encryption with qpdf",
|
|
{$td->COMMAND =>
|
|
"qpdf --copy-encryption=a.pdf".
|
|
" --encryption-file-password=user" .
|
|
" minimal.pdf c.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("check output encryption",
|
|
{$td->COMMAND => "qpdf --show-encryption c.pdf --password=owner"},
|
|
{$td->FILE => "copied-encryption.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("convert to qdf",
|
|
{$td->COMMAND =>
|
|
"qpdf --qdf c.pdf c.qdf" .
|
|
" --password=owner --no-original-object-ids"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("compare qdf",
|
|
{$td->COMMAND => "sh ./diff-ignore-ID-version a.qdf c.qdf"},
|
|
{$td->STRING => "okay\n", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
# Files with attachments
|
|
my @attachments = (
|
|
'enc-XI-attachments-base.pdf',
|
|
'enc-XI-R6,V5,U=attachment,encrypted-attachments.pdf',
|
|
'enc-XI-R6,V5,U=view,attachments,cleartext-metadata.pdf');
|
|
$n_tests += 4 * @attachments + 3;
|
|
foreach my $f (@attachments)
|
|
{
|
|
my $pass = '';
|
|
my $tpass = '';
|
|
if ($f =~ m/U=([^,\.]+)/)
|
|
{
|
|
$pass = "--password=$1";
|
|
$tpass = $1;
|
|
}
|
|
$td->runtest("decrypt $f",
|
|
{$td->COMMAND => "qpdf --decrypt $pass $f a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("extract attachments",
|
|
{$td->COMMAND => "test_driver 35 a.pdf"},
|
|
{$td->FILE => "attachments.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("copy $f",
|
|
{$td->COMMAND => "qpdf $pass $f a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("extract attachments",
|
|
{$td->COMMAND => "test_driver 35 a.pdf $tpass"},
|
|
{$td->FILE => "attachments.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
}
|
|
$td->runtest("unfilterable with crypt",
|
|
{$td->COMMAND =>
|
|
"test_driver 36 unfilterable-with-crypt.pdf attachment"},
|
|
{$td->FILE => "unfilterable-with-crypt-before.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
unlink "a.pdf";
|
|
$td->runtest("decrypt file",
|
|
{$td->COMMAND => "qpdf -decrypt --password=attachment" .
|
|
" unfilterable-with-crypt.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
|
$td->runtest("copy of unfilterable with crypt",
|
|
{$td->COMMAND =>
|
|
"test_driver 36 a.pdf attachment"},
|
|
{$td->FILE => "unfilterable-with-crypt-after.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
# Raw encryption key
|
|
my @enc_key = (['user', '--password=user3'],
|
|
['owner', '--password=owner3'],
|
|
['hex', '--password-is-hex-key --password=35ea16a48b6a3045133b69ac0906c2e8fb0a2cc97903ae17b51a5786ebdba020']);
|
|
$n_tests += scalar(@enc_key);
|
|
foreach my $d (@enc_key)
|
|
{
|
|
my ($description, $pass) = @$d;
|
|
$td->runtest("use/show encryption key ($description)",
|
|
{$td->COMMAND =>
|
|
"qpdf --check --show-encryption-key c-r5-in.pdf $pass"},
|
|
{$td->FILE => "c-r5-key-$description.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
}
|
|
|
|
# Miscellaneous encryption tests
|
|
$n_tests += 3;
|
|
|
|
$td->runtest("set encryption before set filename",
|
|
{$td->COMMAND => "test_driver 63 minimal.pdf"},
|
|
{$td->STRING => "test 63 done\n", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("check file's validity",
|
|
{$td->COMMAND => "qpdf --check --password=u a.pdf"},
|
|
{$td->FILE => "encrypt-before-filename.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("handle missing/invalid Length",
|
|
{$td->COMMAND => "qpdf --check bad-encryption-length.pdf"},
|
|
{$td->FILE => "bad-encryption-length.out",
|
|
$td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
cleanup();
|
|
$td->report($n_tests);
|