<commit_before>#include "mupdf/pdf.h"

enum
{
	PDF_CRYPT_NONE,
	PDF_CRYPT_RC4,
	PDF_CRYPT_AESV2,
	PDF_CRYPT_AESV3,
	PDF_CRYPT_UNKNOWN,
};

typedef struct pdf_crypt_filter_s pdf_crypt_filter;

struct pdf_crypt_filter_s
{
	int method;
	int length;
};

struct pdf_crypt_s
{
	pdf_obj *id;

	int v;
	int length;
	pdf_obj *cf;
	pdf_crypt_filter stmf;
	pdf_crypt_filter strf;

	int r;
	unsigned char o[48];
	unsigned char u[48];
	unsigned char oe[32];
	unsigned char ue[32];
	int p;
	int encrypt_metadata;

	unsigned char key[32]; /* decryption key generated from password */
};

static void pdf_parse_crypt_filter(fz_context *ctx, pdf_crypt_filter *cf, pdf_crypt *crypt, char *name);

/*
 * Create crypt object for decrypting strings and streams
 * given the Encryption and ID objects.
 */

pdf_crypt *
pdf_new_crypt(fz_context *ctx, pdf_obj *dict, pdf_obj *id)
{
	pdf_crypt *crypt;
	pdf_obj *obj;

	crypt = fz_malloc_struct(ctx, pdf_crypt);

	/* Common to all security handlers (PDF 1.7 table 3.18) */

	obj = pdf_dict_gets(dict, "Filter");
	if (!pdf_is_name(obj))
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "unspecified encryption handler");
	}
	if (strcmp(pdf_to_name(obj), "Standard") != 0)
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "unknown encryption handler: '%s'", pdf_to_name(obj));
	}

	crypt->v = 0;
	obj = pdf_dict_gets(dict, "V");
	if (pdf_is_int(obj))
		crypt->v = pdf_to_int(obj);
	if (crypt->v != 1 && crypt->v != 2 && crypt->v != 4 && crypt->v != 5)
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "unknown encryption version");
	}

	/* Standard security handler (PDF 1.7 table 3.19) */

	obj = pdf_dict_gets(dict, "R");
	if (pdf_is_int(obj))
		crypt->r = pdf_to_int(obj);
	else if (crypt->v <= 4)
	{
		fz_warn(ctx, "encryption dictionary missing revision value, guessing...");
		if (crypt->v < 2)
			crypt->r = 2;
		else if (crypt->v == 2)
			crypt->r = 3;
		else if (crypt->v == 4)
			crypt->r = 4;
	}
	else
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing version and revision value");
	}
	if (crypt->r < 1 || crypt->r > 6)
	{
		int r = crypt->r;
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "unknown crypt revision %d", r);
	}

	obj = pdf_dict_gets(dict, "O");
	if (pdf_is_string(obj) && pdf_to_str_len(obj) == 32)
		memcpy(crypt->o, pdf_to_str_buf(obj), 32);
	/* /O and /U are supposed to be 48 bytes long for revision 5 and 6, they're often longer, though */
	else if (crypt->r >= 5 && pdf_is_string(obj) && pdf_to_str_len(obj) >= 48)
		memcpy(crypt->o, pdf_to_str_buf(obj), 48);
	else
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing owner password");
	}

	obj = pdf_dict_gets(dict, "U");
	if (pdf_is_string(obj) && pdf_to_str_len(obj) == 32)
		memcpy(crypt->u, pdf_to_str_buf(obj), 32);
	/* /O and /U are supposed to be 48 bytes long for revision 5 and 6, they're often longer, though */
	else if (crypt->r >= 5 && pdf_is_string(obj) && pdf_to_str_len(obj) >= 48)
		memcpy(crypt->u, pdf_to_str_buf(obj), 48);
	else if (pdf_is_string(obj) && pdf_to_str_len(obj) < 32)
	{
		fz_warn(ctx, "encryption password key too short (%d)", pdf_to_str_len(obj));
		memcpy(crypt->u, pdf_to_str_buf(obj), pdf_to_str_len(obj));
	}
	else
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing user password");
	}

	obj = pdf_dict_gets(dict, "P");
	if (pdf_is_int(obj))
		crypt->p = pdf_to_int(obj);
	else
	{
		fz_warn(ctx, "encryption dictionary missing permissions");
		crypt->p = 0xfffffffc;
	}

	if (crypt->r == 5 || crypt->r == 6)
	{
		obj = pdf_dict_gets(dict, "OE");
		if (!pdf_is_string(obj) || pdf_to_str_len(obj) != 32)
		{
			pdf_free_crypt(ctx, crypt);
			fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing owner encryption key");
		}
		memcpy(crypt->oe, pdf_to_str_buf(obj), 32);

		obj = pdf_dict_gets(dict, "UE");
		if (!pdf_is_string(obj) || pdf_to_str_len(obj) != 32)
		{
			pdf_free_crypt(ctx, crypt);
			fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing user encryption key");
		}
		memcpy(crypt->ue, pdf_to_str_buf(obj), 32);
	}

	crypt->encrypt_metadata = 1;
	obj = pdf_dict_gets(dict, "EncryptMetadata");
	if (pdf_is_bool(obj))
		crypt->encrypt_metadata = pdf_to_bool(obj);

	/* Extract file identifier string */

	if (pdf_is_array(id) && pdf_array_len(id) == 2)
	{
		obj = pdf_array_get(id, 0);
		if (pdf_is_string(obj))
			crypt->id = pdf_keep_obj(obj);
	}
	else
		fz_warn(ctx, "missing file identifier, may not be able to do decryption");

	/* Determine encryption key length */

	crypt->length = 40;
	if (crypt->v == 2)
	{
		obj = pdf_dict_gets(dict, "Length");
		if (pdf_is_int(obj))
			crypt->length = pdf_to_int(obj);

		/* work-around for pdf generators that assume length is in bytes */<commit_msg>Revert part of commit 5<PASSWORD><commit_after>#include "mupdf/pdf.h"

enum
{
	PDF_CRYPT_NONE,
	PDF_CRYPT_RC4,
	PDF_CRYPT_AESV2,
	PDF_CRYPT_AESV3,
	PDF_CRYPT_UNKNOWN,
};

typedef struct pdf_crypt_filter_s pdf_crypt_filter;

struct pdf_crypt_filter_s
{
	int method;
	int length;
};

struct pdf_crypt_s
{
	pdf_obj *id;

	int v;
	int length;
	pdf_obj *cf;
	pdf_crypt_filter stmf;
	pdf_crypt_filter strf;

	int r;
	unsigned char o[48];
	unsigned char u[48];
	unsigned char oe[32];
	unsigned char ue[32];
	int p;
	int encrypt_metadata;

	unsigned char key[32]; /* decryption key generated from password */
};

static void pdf_parse_crypt_filter(fz_context *ctx, pdf_crypt_filter *cf, pdf_crypt *crypt, char *name);

/*
 * Create crypt object for decrypting strings and streams
 * given the Encryption and ID objects.
 */

pdf_crypt *
pdf_new_crypt(fz_context *ctx, pdf_obj *dict, pdf_obj *id)
{
	pdf_crypt *crypt;
	pdf_obj *obj;

	crypt = fz_malloc_struct(ctx, pdf_crypt);

	/* Common to all security handlers (PDF 1.7 table 3.18) */

	obj = pdf_dict_gets(dict, "Filter");
	if (!pdf_is_name(obj))
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "unspecified encryption handler");
	}
	if (strcmp(pdf_to_name(obj), "Standard") != 0)
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "unknown encryption handler: '%s'", pdf_to_name(obj));
	}

	crypt->v = 0;
	obj = pdf_dict_gets(dict, "V");
	if (pdf_is_int(obj))
		crypt->v = pdf_to_int(obj);
	if (crypt->v != 1 && crypt->v != 2 && crypt->v != 4 && crypt->v != 5)
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "unknown encryption version");
	}

	/* Standard security handler (PDF 1.7 table 3.19) */

	obj = pdf_dict_gets(dict, "R");
	if (pdf_is_int(obj))
		crypt->r = pdf_to_int(obj);
	else if (crypt->v <= 4)
	{
		fz_warn(ctx, "encryption dictionary missing revision value, guessing...");
		if (crypt->v < 2)
			crypt->r = 2;
		else if (crypt->v == 2)
			crypt->r = 3;
		else if (crypt->v == 4)
			crypt->r = 4;
	}
	else
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing version and revision value");
	}
	if (crypt->r < 1 || crypt->r > 6)
	{
		int r = crypt->r;
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "unknown crypt revision %d", r);
	}

	obj = pdf_dict_gets(dict, "O");
	if (pdf_is_string(obj) && pdf_to_str_len(obj) == 32)
		memcpy(crypt->o, pdf_to_str_buf(obj), 32);
	/* /O and /U are supposed to be 48 bytes long for revision 5 and 6, they're often longer, though */
	else if (crypt->r >= 5 && pdf_is_string(obj) && pdf_to_str_len(obj) >= 48)
		memcpy(crypt->o, pdf_to_str_buf(obj), 48);
	else
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing owner password");
	}

	obj = pdf_dict_gets(dict, "U");
	if (pdf_is_string(obj) && pdf_to_str_len(obj) == 32)
		memcpy(crypt->u, pdf_to_str_buf(obj), 32);
	/* /O and /U are supposed to be 48 bytes long for revision 5 and 6, they're often longer, though */
	else if (crypt->r >= 5 && pdf_is_string(obj) && pdf_to_str_len(obj) >= 48)
		memcpy(crypt->u, pdf_to_str_buf(obj), 48);
	else if (pdf_is_string(obj) && pdf_to_str_len(obj) < 32)
	{
		fz_warn(ctx, "encryption password key too short (%d)", pdf_to_str_len(obj));
		memcpy(crypt->u, pdf_to_str_buf(obj), pdf_to_str_len(obj));
	}
	else
	{
		pdf_free_crypt(ctx, crypt);
		fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing user password");
	}

	obj = pdf_dict_gets(dict, "P");
	if (pdf_is_int(obj))
		crypt->p = pdf_to_int(obj);
	else
	{
		fz_warn(ctx, "encryption dictionary missing permissions");
		crypt->p = 0xfffffffc;
	}

	if (crypt->r == 5 || crypt->r == 6)
	{
		obj = pdf_dict_gets(dict, "OE");
		if (!pdf_is_string(obj) || pdf_to_str_len(obj) != 32)
		{
			pdf_free_crypt(ctx, crypt);
			fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing owner encryption key");
		}
		memcpy(crypt->oe, pdf_to_str_buf(obj), 32);

		obj = pdf_dict_gets(dict, "UE");
		if (!pdf_is_string(obj) || pdf_to_str_len(obj) != 32)
		{
			pdf_free_crypt(ctx, crypt);
			fz_throw(ctx, FZ_ERROR_GENERIC, "encryption dictionary missing user encryption key");
		}
		memcpy(crypt->ue, pdf_to_str_buf(obj), 32);
	}

	crypt->encrypt_metadata = 1;
	obj = pdf_dict_gets(dict, "EncryptMetadata");
	if (pdf_is_bool(obj))
		crypt->encrypt_metadata = pdf_to_bool(obj);

	/* Extract file identifier string */

	if (pdf_is_array(id) && pdf_array_len(id) == 2)
	{
		obj = pdf_array_get(id, 0);
		if (pdf_is_string(obj))
			crypt->id = pdf_keep_obj(obj);
	}
	else
		fz_warn(ctx, "missing file identifier, may not be able to do decryption");

	/* Determine encryption key length */

	crypt->length = 40;
	if (crypt->v == 2 || crypt->v == 4)
	{
		obj = pdf_dict_gets(dict, "Length");
		if (pdf_is_int(obj))
			crypt->length = pdf_to_int(obj);

		/* work-around for pdf generators that assume length is in bytes */
{'fraction_non_alphanumeric': 0.1487603305785124, 'fraction_numerical': 0.017141108050198958, 'mean_word_length': 4.4759776536312845, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7622594', 'n_tokens_mistral': 4354, 'n_tokens_neox': 3887, 'n_words': 930}