ladybird/Userland/Libraries/LibPDF/Encryption.h
Nico Weber 0428308420 LibPDF: Implement 7.6.4.3.3 Algorithm 2.A: Retrieve file encryption key
...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.
2023-07-21 11:55:20 +02:00

98 lines
3 KiB
C++

/*
* Copyright (c) 2022, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Span.h>
#include <LibPDF/ObjectDerivatives.h>
namespace Crypto::Cipher {
enum class Intent;
}
namespace PDF {
enum class CryptFilterMethod {
None,
V2, // RC4
AESV2,
AESV3,
};
class SecurityHandler : public RefCounted<SecurityHandler> {
public:
static PDFErrorOr<NonnullRefPtr<SecurityHandler>> create(Document*, NonnullRefPtr<DictObject> encryption_dict);
virtual ~SecurityHandler() = default;
virtual bool try_provide_user_password(StringView password) = 0;
virtual bool has_user_password() const = 0;
virtual void encrypt(NonnullRefPtr<Object>, Reference reference) const = 0;
virtual void decrypt(NonnullRefPtr<Object>, Reference reference) const = 0;
};
class StandardSecurityHandler : public SecurityHandler {
public:
static PDFErrorOr<NonnullRefPtr<StandardSecurityHandler>> create(Document*, NonnullRefPtr<DictObject> encryption_dict);
StandardSecurityHandler(Document*, size_t revision, DeprecatedString const& o_entry, DeprecatedString const& oe_entry, DeprecatedString const& u_entry, DeprecatedString const& ue_entry, DeprecatedString const& perms, u32 flags, bool encrypt_metadata, size_t length, CryptFilterMethod method);
~StandardSecurityHandler() override = default;
bool try_provide_user_password(StringView password_string) override;
bool has_user_password() const override { return m_encryption_key.has_value(); }
protected:
void encrypt(NonnullRefPtr<Object>, Reference reference) const override;
void decrypt(NonnullRefPtr<Object>, Reference reference) const override;
private:
void crypt(NonnullRefPtr<Object>, Reference reference, Crypto::Cipher::Intent) const;
ByteBuffer compute_user_password_value_r2(ByteBuffer password_string);
ByteBuffer compute_user_password_value_r3_to_r5(ByteBuffer password_string);
bool authenticate_user_password_r2_to_r5(StringView password_string);
bool authenticate_user_password_r6_and_later(StringView password_string);
bool authenticate_owner_password_r6_and_later(StringView password_string);
ByteBuffer compute_encryption_key_r2_to_r5(ByteBuffer password_string);
bool compute_encryption_key_r6_and_later(ByteBuffer password_string);
enum class HashKind {
Owner,
User,
};
ByteBuffer computing_a_hash_r6_and_later(ByteBuffer input, StringView input_password, HashKind);
Document* m_document;
size_t m_revision;
Optional<ByteBuffer> m_encryption_key;
DeprecatedString m_o_entry;
DeprecatedString m_oe_entry;
DeprecatedString m_u_entry;
DeprecatedString m_ue_entry;
DeprecatedString m_perms_entry;
u32 m_flags;
bool m_encrypt_metadata;
size_t m_length;
CryptFilterMethod m_method;
};
class RC4 {
public:
RC4(ReadonlyBytes key);
void generate_bytes(ByteBuffer&);
ByteBuffer encrypt(ReadonlyBytes bytes);
private:
Array<size_t, 256> m_bytes;
};
}