1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 03:17:35 +00:00

LibPDF: Some preparatory work for AESV3

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/
This commit is contained in:
Nico Weber 2023-07-13 21:35:26 -04:00 committed by Andreas Kling
parent b14d51ba1b
commit 281e3158c0
2 changed files with 152 additions and 21 deletions

View file

@ -65,7 +65,7 @@ struct CryptFilter {
int length_in_bits { 0 };
};
static PDFErrorOr<CryptFilter> parse_v4_crypt(Document* document, NonnullRefPtr<DictObject> encryption_dict, DeprecatedString filter)
static PDFErrorOr<CryptFilter> parse_v4_or_newer_crypt(Document* document, NonnullRefPtr<DictObject> encryption_dict, DeprecatedString filter)
{
// See 3.5 Encryption, Table 3.18 "Entries common to all encryption dictionaries" for StmF and StrF,
// and 3.5.4 Crypt Filters in the 1.7 spec, in particular Table 3.22 "Entries common to all crypt filter dictionaries".
@ -103,12 +103,19 @@ static PDFErrorOr<CryptFilter> parse_v4_crypt(Document* document, NonnullRefPtr<
return CryptFilter { CryptFilterMethod::V2, length_in_bits };
if (crypt_filter_method == "AESV2") {
// "the AES algorithm in Cipher Block Chaining (CBC) mode with a 16-byte block size"
// "the AES algorithm in Cipher Block Chaining (CBC) mode with a 16-byte block size [...] The key size (Length) shall be 128 bits."
if (length_in_bits != 128)
return Error(Error::Type::Parse, "Unexpected bit size for AESV2");
return CryptFilter { CryptFilterMethod::AESV2, length_in_bits };
}
if (crypt_filter_method == "AESV3") {
// "the AES-256 algorithm in Cipher Block Chaining (CBC) with padding mode with a 16-byte block size [...] The key size (Length) shall be 256 bits."
if (length_in_bits != 256)
return Error(Error::Type::Parse, "Unexpected bit size for AESV3");
return CryptFilter { CryptFilterMethod::AESV3, length_in_bits };
}
return Error(Error::Type::Parse, "Unknown crypt filter method");
}
@ -128,7 +135,7 @@ PDFErrorOr<NonnullRefPtr<StandardSecurityHandler>> StandardSecurityHandler::crea
auto method = CryptFilterMethod::V2;
size_t length_in_bits = 40;
if (v == 4) {
if (v >= 4) {
// "Default value: Identity"
DeprecatedString stream_filter = "Identity";
if (encryption_dict->contains(CommonNames::StmF))
@ -141,7 +148,7 @@ PDFErrorOr<NonnullRefPtr<StandardSecurityHandler>> StandardSecurityHandler::crea
if (stream_filter != string_filter)
return Error(Error::Type::Parse, "Can't handle StmF and StrF being different");
auto crypt_filter = TRY(parse_v4_crypt(document, encryption_dict, stream_filter));
auto crypt_filter = TRY(parse_v4_or_newer_crypt(document, encryption_dict, stream_filter));
method = crypt_filter.method;
length_in_bits = crypt_filter.length_in_bits;
} else if (encryption_dict->contains(CommonNames::Length))
@ -172,14 +179,14 @@ StandardSecurityHandler::StandardSecurityHandler(Document* document, size_t revi
{
}
ByteBuffer StandardSecurityHandler::compute_user_password_value_v2(ByteBuffer password_string)
ByteBuffer StandardSecurityHandler::compute_user_password_value_r2(ByteBuffer password_string)
{
// Algorithm 4: Computing the encryption dictionary's U (user password)
// value (Security handlers of revision 2)
// a) Create an encryption key based on the user password string, as
// described in [Algorithm 2]
auto encryption_key = compute_encryption_key(password_string);
auto encryption_key = compute_encryption_key_r2_to_r5(password_string);
// b) Encrypt the 32-byte padding string shown in step (a) of [Algorithm 2],
// using an RC4 encryption function with the encryption key from the
@ -192,14 +199,14 @@ ByteBuffer StandardSecurityHandler::compute_user_password_value_v2(ByteBuffer pa
return output;
}
ByteBuffer StandardSecurityHandler::compute_user_password_value_v3_and_newer(ByteBuffer password_string)
ByteBuffer StandardSecurityHandler::compute_user_password_value_r3_to_r5(ByteBuffer password_string)
{
// Algorithm 5: Computing the encryption dictionary's U (user password)
// value (Security handlers of revision 3 or greater)
// a) Create an encryption key based on the user password string, as
// described in [Algorithm 2]
auto encryption_key = compute_encryption_key(password_string);
auto encryption_key = compute_encryption_key_r2_to_r5(password_string);
// b) Initialize the MD5 hash function and pass the 32-byte padding string
// shown in step (a) of [Algorithm 2] as input to this function
@ -244,7 +251,7 @@ ByteBuffer StandardSecurityHandler::compute_user_password_value_v3_and_newer(Byt
return buffer;
}
bool StandardSecurityHandler::try_provide_user_password(StringView password_string)
bool StandardSecurityHandler::authenticate_user_password_r2_to_r5(StringView password_string)
{
// Algorithm 6: Authenticating the user password
@ -252,9 +259,9 @@ bool StandardSecurityHandler::try_provide_user_password(StringView password_stri
// supplied password string.
ByteBuffer password_buffer = MUST(ByteBuffer::copy(password_string.bytes()));
if (m_revision == 2) {
password_buffer = compute_user_password_value_v2(password_buffer);
password_buffer = compute_user_password_value_r2(password_buffer);
} else {
password_buffer = compute_user_password_value_v3_and_newer(password_buffer);
password_buffer = compute_user_password_value_r3_to_r5(password_buffer);
}
// b) If the result of step (a) is equal to the value of the encryption
@ -262,17 +269,38 @@ bool StandardSecurityHandler::try_provide_user_password(StringView password_stri
// handlers of revision 3 or greater), the password supplied is the correct user
// password.
auto u_bytes = m_u_entry.bytes();
bool has_user_password;
if (m_revision >= 3)
has_user_password = u_bytes.slice(0, 16) == password_buffer.bytes().slice(0, 16);
return u_bytes.slice(0, 16) == password_buffer.bytes().slice(0, 16);
return u_bytes == password_buffer.bytes();
}
bool StandardSecurityHandler::authenticate_user_password_r6_and_later(StringView)
{
// ISO 32000 (PDF 2.0), 7.6.4.4.10 Algorithm 11: Authenticating the user password (Security handlers of
// revision 6)
// a) Test the password against the user key by computing the 32-byte hash using 7.6.4.3.4, "Algorithm 2.B:
// Computing a hash (revision 6 or later)" with an input string consisting of the UTF-8 password
// concatenated with the 8 bytes of User Validation Salt (see 7.6.4.4.7, "Algorithm 8: Computing the
// encryption dictionary's U (user password) and UE (user encryption) values (Security handlers of
// revision 6)"). If the 32- byte result matches the first 32 bytes of the U string, this is the user password.
TODO();
}
bool StandardSecurityHandler::try_provide_user_password(StringView password_string)
{
bool has_user_password;
if (m_revision >= 6)
has_user_password = authenticate_user_password_r6_and_later(password_string);
else
has_user_password = u_bytes == password_buffer.bytes();
has_user_password = authenticate_user_password_r2_to_r5(password_string);
if (!has_user_password)
m_encryption_key = {};
return has_user_password;
}
ByteBuffer StandardSecurityHandler::compute_encryption_key(ByteBuffer password_string)
ByteBuffer StandardSecurityHandler::compute_encryption_key_r2_to_r5(ByteBuffer password_string)
{
// This function should never be called after we have a valid encryption key.
VERIFY(!m_encryption_key.has_value());
@ -369,16 +397,113 @@ ByteBuffer StandardSecurityHandler::compute_encryption_key(ByteBuffer password_s
return encryption_key;
}
ByteBuffer StandardSecurityHandler::compute_encryption_key_r6_and_later(ByteBuffer password_string)
{
// This function should never be called after we have a valid encryption key.
VERIFY(!m_encryption_key.has_value());
// ISO 32000 (PDF 2.0), 7.6.4.3.3 Algorithm 2.A: Retrieving the file encryption key from an encrypted
// document in order to decrypt it (revision 6 or later)
// "It is necessary to treat the 48-bytes of the O and U strings in the
// Encrypt dictionary as made up of three sections [...]. The first 32 bytes
// are a hash value (explained below). The next 8 bytes are called the Validation Salt. The final 8 bytes are
// called the Key Salt."
// a) The UTF-8 password string shall be generated from Unicode input by processing the input string with
// the SASLprep (Internet RFC 4013) profile of stringprep (Internet RFC 3454) using the Normalize and BiDi
// options, and then converting to a UTF-8 representation.
// FIXME
// b) Truncate the UTF-8 representation to 127 bytes if it is longer than 127 bytes.
if (password_string.size() > 127)
password_string.resize(127);
// c) Test the password against the owner key by computing a hash using algorithm 2.B with an input string
// consisting of the UTF-8 password concatenated with the 8 bytes of owner Validation Salt, concatenated
// with the 48-byte U string. If the 32-byte result matches the first 32 bytes of the O string, this is the owner
// password.
// d) Compute an intermediate owner key by computing a hash using algorithm 2.B with an input string
// consisting of the UTF-8 owner password concatenated with the 8 bytes of owner Key Salt, concatenated
// with the 48-byte U string. The 32-byte result is the key used to decrypt the 32-byte OE string using AES-
// 256 in CBC mode with no padding and an initialization vector of zero. The 32-byte result is the file
// encryption key.
// e) Compute an intermediate user key by computing a hash using algorithm 2.B with an input string
// consisting of the UTF-8 user password concatenated with the 8 bytes of user Key Salt. The 32-byte result
// is the key used to decrypt the 32-byte UE string using AES-256 in CBC mode with no padding and an
// initialization vector of zero. The 32-byte result is the file encryption key.
// f) Decrypt the 16-bye Perms string using AES-256 in ECB mode with an initialization vector of zero and
// the file encryption key as the key. Verify that bytes 9-11 of the result are the characters "a", "d", "b". Bytes
// 0-3 of the decrypted Perms entry, treated as a little-endian integer, are the user permissions. They shall
// match the value in the P key.
TODO();
}
ByteBuffer StandardSecurityHandler::computing_a_hash_r6_and_later(ByteBuffer)
{
// ISO 32000 (PDF 2.0), 7.6.4.3.4 Algorithm 2.B: Computing a hash (revision 6 or later)
// Take the SHA-256 hash of the original input to the algorithm and name the resulting 32 bytes, K.
// Perform the following steps (a)-(d) 64 times:
// a) Make a new string, K1, consisting of 64 repetitions of the sequence: Input password, K, the 48-byte user
// key. The 48 byte user key is only used when checking the owner password or creating the owner key. If
// checking the user password or creating the user key, K1 is the concatenation of the input password and K.
// b) Encrypt K1 with the AES-128 (CBC, no padding) algorithm, using the first 16 bytes of K as the key and
// the second 16 bytes of K as the initialization vector. The result of this encryption is E.
// c) Taking the first 16 bytes of E as an unsigned big-endian integer, compute the remainder, modulo 3. If the
// result is 0, the next hash used is SHA-256, if the result is 1, the next hash used is SHA-384, if the result is
// 2, the next hash used is SHA-512.
// d) Using the hash algorithm determined in step c, take the hash of E. The result is a new value of K, which
// will be 32, 48, or 64 bytes in length.
// Repeat the process (a-d) with this new value of K. Following 64 rounds (round number 0 to round
// number 63), do the following, starting with round number 64:
// NOTE 2 The reason for multiple rounds is to defeat the possibility of running all paths in parallel. With 64
// rounds (minimum) there are 3^64 paths through the algorithm.
// e) Look at the very last byte of E. If the value of that byte (taken as an unsigned integer) is greater than the
// round number - 32, repeat steps (a-d) again.
// f) Repeat from steps (a-e) until the value of the last byte is <= (round number) - 32.
// NOTE 3 Tests indicate that the total number of rounds will most likely be between 65 and 80.
// The first 32 bytes of the final K are the output of the algorithm.
TODO();
}
void StandardSecurityHandler::crypt(NonnullRefPtr<Object> object, Reference reference, Crypto::Cipher::Intent direction) const
{
// 7.6.2 General Encryption Algorithm
// Algorithm 1: Encryption of data using the RC3 or AES algorithms
VERIFY(m_encryption_key.has_value());
if (m_method == CryptFilterMethod::None)
return;
if (m_method == CryptFilterMethod::AESV3) {
// ISO 32000 (PDF 2.0), 7.6.3.3 Algorithm 1.A: Encryption of data using the AES algorithms
// a) Use the 32-byte file encryption key for the AES-256 symmetric key algorithm, along with the string or
// stream data to be encrypted.
//
// Use the AES algorithm in Cipher Block Chaining (CBC) mode, which requires an initialization
// vector. The block size parameter is set to 16 bytes, and the initialization vector is a 16-byte random
// number that is stored as the first 16 bytes of the encrypted stream or string.
TODO();
}
// 7.6.2 General Encryption Algorithm
// Algorithm 1: Encryption of data using the RC3 or AES algorithms
// a) Obtain the object number and generation number from the object identifier of
// the string or stream to be encrypted. If the string is a direct object, use
// the identifier of the indirect object containing it.