diff options
Diffstat (limited to 'qpdf/qtest/qpdf.test')
-rw-r--r-- | qpdf/qtest/qpdf.test | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 45f2a6f4..1ccd4d12 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -3222,6 +3222,188 @@ foreach my $d (@enc_key) show_ntests(); # ---------- +$td->notify("--- Unicode Passwords ---"); +# $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 = $decode_cases; + $decode_cases = []; + } + $td->runtest("encode $bits, $pw, $w_encoding", + {$td->COMMAND => + "qpdf $xargs --static-id --static-aes-iv" . + " --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; + if ($xfail) + { + $r_output .= "a.pdf: invalid password\n"; + } + 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 --show-encryption --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 += 2; +$td->runtest("bytes fallback warning", + {$td->COMMAND => + "qpdf --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); +} + +show_ntests(); +# ---------- $td->notify("--- Check from C API ---"); my @c_check_types = qw(warn clear); $n_tests += scalar(@c_check_types); |