With this, `pdf` can print info for CIPA_DC-003-2020_E.pdf
(from https://cipa.jp/e/std/std-sec.html), as well as all other
files I've tried.
CIPA_DC-003-2020_E.pdf is special because it quits this loop after
exactly 64 interations, at round_number 63.
While here, also update a comment to use the non-spec-comment style
I'm now using elsewhere in the file.
With this, AESV3 support is complete and CIPA_DC-007-2021_E.pdf
can be opened :^)
(CIPA_DC-003-2020_E.pdf incorrectly cannot be opened yet. This
is due to a minor bug in computing_a_hash_r6_and_later() that
I'll fix a bit later. But except for this minor bug, all AESV3
files I've found so far seem to work.)
try_provide_user_password() calls compute_encryption_key_r6_and_later()
now. This checks both owner and user passwords. (For pre-R6 files,
owner password checking isn't yet implemented, as far as I can tell.)
With this, CIPA_DC-007-2021_E.pdf (or other AESV3-encrypted files)
successfully compute a file encryption key (...and then hit the
TODO() in StandardSecurityHandler::crypt() for AESV3, but it's
still good progress.)
...for handlers of revision 6.
The spec for this algorithm has several quirks:
1. It describes how to authenticate a password as an owner password,
but it redundantly inlines the description of algorithm 12 instead
of referring to it. We just call that algorithm here.
2. It does _not_ describe how to authenticate a password as a user
password before using the password to compute the file encryption
key using an intermediate user key, despite the latter step that
computes the file encryption key refers to the password as
"user password". I added a call to algorithm 11 to check if the
password is the user password that isn't in the spec. Maybe I'm
misunderstanding the spec, but this looks like a spec bug to me.
3. It says "using AES-256 in ECB mode with an initialization vector
of zero". ECB mode has no initialization vector. CBC mode with
initialization vector of zero for message length 16 is the same
as ECB mode though, so maybe that's meant? (In addition to the
spec being a bit wobbly, using EBC in new software isn't
recommended, but too late for that.)
SASLprep / stringprep still aren't implemented. For ASCII passwords
(including the important empty password), this is good enough.
...for handlers of revision 6.
Since this adds U to the hash input, also trim the size of U and O to
48 bytes. The spec requires them to be 48 bytes, but all the newer PDFs
on https://cipa.jp/e/std/std-sec.html have 127 bytes -- 48 real bytes
and 79 nul padding bytes. These files were created by:
Creator: Word 用 Acrobat PDFMaker 17
Producer: Adobe PDF Library 15.0
and
Creator: Word 用 Acrobat PDFMaker 17
Producer: Adobe PDF Library 17.11.238
This is a step towards AESV3 support for PDF files.
The straight-forward way of writing this with our APIs is pretty
allocation-heavy, but this code won't run all that often for the
regular "open PDF, check password" flow.
- `encrypt()` will always fill a multiple of block size,
`decrypt()` might produce less data. But other than that,
the middle span isn't modified even though it's a reference.
So pass the ByteBuffer to assign() (kind of like before 5998072f15,
but pass-by-move())
- In the encryption code path, assign a single buffer for IV and data
instead of awkwardly copying the data around later.
Thanks to CxByte for suggesting most of this!
No intentional behavior change.
This detects AESV3, and copies over the spec comments explaining what
needs to be done, but doesn't actually do it yet.
AESV3 is technically PDF 2.0-only, but
https://cipa.jp/std/documents/download_e.html?CIPA_DC-007-2021_E has a
1.7 PDF that uses it.
Previously we'd claim that we need a password to decrypt it.
Now, we cleanly crash with a TODO() \o/
Two lambdas were capturing locals that were out of scope by the
time the lambdas ran.
With this, `pdf` can successfully load and print the page count of
pdf_reference_1.7.pdf.
Before this patch, the generation of the encryption key was not working
correctly since the lifetime of the underlying data was too short,
same inputs would give random encryption keys.
Fixes#16668
When an attempt is made to provide the user password to a
SecurityHandler a user gets back a boolean result indicating success or
failure on the attempt. However, the SecurityHandler is left in a state
where it thinks it has a user password, regardless of the outcome of the
attempt. This confuses the rest of the system, which continues as if the
provided password is correct, resulting in garbled content.
This commit fixes the situation by resetting the internal fields holding
the encryption key (which is used to determine whether a user password
has been successfully provided) in case of a failed attempt.
With the StandardSecurityHandler the Length item in the Encryption
dictionary is optional, and needs to be given only if the encryption
algorithm (V) is other than 1; otherwise we can assume a length of 40
bits for the encryption key.
We have a new, improved string type coming up in AK (OOM aware, no null
state), and while it's going to use UTF-8, the name UTF8String is a
mouthful - so let's free up the String name by renaming the existing
class.
Making the old one have an annoying name will hopefully also help with
quick adoption :^)
Security handlers manage encryption and decription of PDF files. The
standard security handler uses RC4/MD5 to perform its crypto (AES as
well, but that is not yet implemented).