mirror of
https://github.com/qpdf/qpdf.git
synced 2024-09-28 04:59:05 +00:00
221 lines
7.5 KiB
Perl
221 lines
7.5 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('unicode-password');
|
|
|
|
my $n_tests = 0;
|
|
# $n_tests incremented below
|
|
|
|
# Files with each of these passwords when properly encoded have been
|
|
# tested manually with multiple PDF viewers. Adobe Reader, chrome,
|
|
# xpdf, and gv can open all of them except R3 with "single-byte",
|
|
# which can be opened by xpdf and gv but not the others. As of
|
|
# 2019-01-19, okular and atril (evince) are not able to open R=6 files
|
|
# with Unicode passwords as generated by qpdf but can open the R=3
|
|
# files.
|
|
|
|
# [bits, password-or-password-name, write-encoding, actual-encoding, xargs,
|
|
# [[read-encoding, strict?, fail?, tried-others, xargs]]]
|
|
my @unicode_pw_cases = (
|
|
[128, 'simple', 'pdf-doc', 'pdf-doc', '',
|
|
[['utf8', 0, 0, 1, ''],
|
|
['utf8', 1, 1, 0, ''],
|
|
['pdf-doc', 1, 0, 0, ''],
|
|
]],
|
|
[128, 'simple', 'utf8', 'utf8', '--password-mode=bytes',
|
|
[['pdf-doc', 0, 0, 1, ''],
|
|
['pdf-doc', 1, 1, 0, ''],
|
|
['utf8', 1, 0, 0, ''],
|
|
]],
|
|
[128, 'simple', 'utf8', 'pdf-doc', '--password-mode=unicode',
|
|
[['pdf-doc', 1, 0, 0, ''],
|
|
]],
|
|
[128, 'simple', 'utf8', 'pdf-doc', '--password-mode=auto',
|
|
[['pdf-doc', 1, 0, 0, ''],
|
|
]],
|
|
[128, 'single-byte', 'utf8', 'pdf-doc', '',
|
|
[['pdf-doc', 1, 0, 0, ''],
|
|
['win-ansi', 0, 0, 1, ''],
|
|
]],
|
|
[128, 'single-byte', 'utf8', 'pdf-doc', '--password-mode=unicode',
|
|
[['pdf-doc', 1, 0, 0, ''],
|
|
['win-ansi', 0, 0, 1, ''],
|
|
]],
|
|
[128, 'single-byte', 'win-ansi', '', '--password-mode=unicode',
|
|
"supplied password is not valid UTF-8\n",
|
|
],
|
|
[128, 'single-byte', 'win-ansi', 'win-ansi', '',
|
|
[['win-ansi', 1, 0, 0, ''],
|
|
]],
|
|
[128, 'single-byte', 'pdf-doc', 'pdf-doc', '',
|
|
[['pdf-doc', 1, 0, 0, ''],
|
|
['win-ansi', 0, 0, 1, ''],
|
|
['pdf-doc-hex', 1, 0, 0, '--password-mode=hex-bytes'],
|
|
]],
|
|
[128, 'complex', 'utf8', '', '--password-mode=unicode',
|
|
"supplied password cannot be encoded for 40-bit or" .
|
|
" 128-bit encryption formats\n"
|
|
],
|
|
[128, 'complex', 'utf8', 'utf8', '--password-mode=bytes',
|
|
[['utf8', 1, 0, 0, ''],
|
|
]],
|
|
[256, 'single-byte', 'win-ansi', '', '--password-mode=unicode',
|
|
"supplied password is not valid UTF-8\n",
|
|
],
|
|
[256, 'single-byte', 'win-ansi', '', '--password-mode=auto',
|
|
"supplied password is not a valid Unicode password, which is" .
|
|
" required for 256-bit encryption; to really use this password," .
|
|
" rerun with the --password-mode=bytes option\n",
|
|
],
|
|
[256, 'single-byte', 'win-ansi', 'win-ansi', '--password-mode=bytes',
|
|
[['utf8', 0, 0, 1, ''],
|
|
['utf8', 1, 1, 0, ''],
|
|
['win-ansi', 1, 0, 0, ''],
|
|
['win-ansi', 0, 0, 0, ''],
|
|
['pdf-doc', 0, 0, 1, ''],
|
|
['pdf-doc-hex', 0, 0, 1, '--password-mode=hex-bytes'],
|
|
]],
|
|
[256, 'complex', 'utf8', 'utf8', '',
|
|
[['utf8', 1, 0, 0, ''],
|
|
['utf8-hex', 1, 0, 0, '--password-mode=hex-bytes'],
|
|
]],
|
|
[256, 'complex', 'utf8-hex', 'utf8', '--password-mode=hex-bytes',
|
|
[['utf8', 1, 0, 0, ''],
|
|
['utf8-hex', 1, 0, 0, '--password-mode=hex-bytes'],
|
|
]],
|
|
[256, 'complex', 'utf8', 'utf8', '--password-mode=unicode',
|
|
[['utf8', 1, 0, 0, ''],
|
|
['password-arg-simple-utf8', 0, 1, 1, ''],
|
|
]],
|
|
);
|
|
|
|
for my $d (@unicode_pw_cases)
|
|
{
|
|
my $decode_cases = $d->[5];
|
|
$n_tests += 1;
|
|
if (ref($decode_cases) eq 'ARRAY')
|
|
{
|
|
$n_tests += scalar(@$decode_cases);
|
|
}
|
|
}
|
|
|
|
foreach my $d (@unicode_pw_cases)
|
|
{
|
|
my ($bits, $pw, $w_encoding, $a_encoding, $xargs, $decode_cases) = @$d;
|
|
my $w_pfile = "password-bare-$pw-$w_encoding";
|
|
my $upass;
|
|
if (-f $w_pfile)
|
|
{
|
|
$upass = '@' . $w_pfile;
|
|
}
|
|
else
|
|
{
|
|
$upass = "$pw";
|
|
}
|
|
my $outbase = "unicode-pw-$bits-$pw-$w_encoding-$xargs";
|
|
my $exp = '';
|
|
if (ref($decode_cases) ne 'ARRAY')
|
|
{
|
|
$exp = "qpdf: $decode_cases";
|
|
$decode_cases = [];
|
|
}
|
|
$td->runtest("encode $bits, $pw, $w_encoding",
|
|
{$td->COMMAND =>
|
|
"qpdf $xargs --static-id --static-aes-iv" .
|
|
" --allow-weak-crypto" .
|
|
" --encrypt $upass o $bits -- minimal.pdf a.pdf"},
|
|
{$td->STRING => $exp, $td->EXIT_STATUS => ($exp ? 2 : 0)},
|
|
$td->NORMALIZE_NEWLINES);
|
|
foreach my $d2 (@$decode_cases)
|
|
{
|
|
my ($r_encoding, $strict, $xfail, $tried_others, $r_xargs) = @$d2;
|
|
my $r_pfile = "password-arg-$pw-$r_encoding";
|
|
if (! -f $r_pfile)
|
|
{
|
|
$r_pfile = $r_encoding;
|
|
}
|
|
my $r_output = "";
|
|
$r_output .= "trying other\n" if $tried_others;
|
|
my $arg = "--show-encryption";
|
|
if ($xfail)
|
|
{
|
|
$r_output .= "qpdf: a.pdf: invalid password\n";
|
|
$arg = "--check";
|
|
}
|
|
else
|
|
{
|
|
$r_output .= "R = " . ($bits == 128 ? '3' : '6') . "\n";
|
|
open(F, "<password-bare-$pw-$a_encoding") or die;
|
|
chomp (my $apw = <F>);
|
|
close(F);
|
|
$r_output .= "User password = $apw\n";
|
|
}
|
|
$r_xargs .= $strict ? ' --suppress-password-recovery' : '';
|
|
$td->runtest("decrypt $pw, $r_encoding, strict=$strict",
|
|
{$td->COMMAND =>
|
|
"qpdf $arg --verbose" .
|
|
" $r_xargs a.pdf \@$r_pfile",
|
|
$td->FILTER => "perl show-unicode-encryption.pl"},
|
|
{$td->STRING => "$r_output",
|
|
$td->EXIT_STATUS => ($xfail ? 2 : 0)},
|
|
$td->NORMALIZE_NEWLINES);
|
|
}
|
|
}
|
|
|
|
$n_tests += 5;
|
|
|
|
$td->runtest("bytes fallback warning",
|
|
{$td->COMMAND =>
|
|
"qpdf --allow-weak-crypto" .
|
|
" --encrypt \@password-bare-complex-utf8 o 128 --" .
|
|
" minimal.pdf a.pdf"},
|
|
{$td->FILE => "bytes-fallback.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
{ # local scope
|
|
my $r_output = "";
|
|
$r_output .= "R = 3\n";
|
|
open(F, "<password-bare-complex-utf8") or die;
|
|
chomp (my $apw = <F>);
|
|
close(F);
|
|
$r_output .= "User password = $apw\n";
|
|
$td->runtest("decrypt bytes fallback",
|
|
{$td->COMMAND =>
|
|
"qpdf --show-encryption --verbose" .
|
|
" a.pdf \@password-arg-complex-utf8" .
|
|
" --password-mode=bytes",
|
|
$td->FILTER => "perl show-unicode-encryption.pl"},
|
|
{$td->STRING => "$r_output", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
}
|
|
|
|
# Exercise passing Unicode passwords via the command line. This tests
|
|
# wmain for Windows and assumes a UTF-8 locale for other platforms.
|
|
$td->runtest("Unicode at CLI",
|
|
{$td->COMMAND =>
|
|
"qpdf --encrypt π ʬ 256 --" .
|
|
" minimal.pdf a.pdf"},
|
|
{$td->STRING => "", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("Decrypt using user password",
|
|
{$td->COMMAND => "qpdf --show-encryption a.pdf --password=π"},
|
|
{$td->FILE => "unicode-up.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
$td->runtest("Decrypt using owner password",
|
|
{$td->COMMAND => "qpdf --show-encryption a.pdf --password=ʬ"},
|
|
{$td->FILE => "unicode-op.out", $td->EXIT_STATUS => 0},
|
|
$td->NORMALIZE_NEWLINES);
|
|
|
|
cleanup();
|
|
$td->report($n_tests);
|