summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIru Cai <mytbk920423@gmail.com>2018-11-18 23:37:45 +0800
committerIru Cai <mytbk920423@gmail.com>2018-11-24 15:37:16 +0800
commit918467458d7572e5d54fc93f60d71c8d80f368f6 (patch)
tree3b70e8526d5dc61e5690d6834b100ac3fe4e53ef
parent7f77313e6d60fc917146b80cce2f9c1ab0152349 (diff)
downloadmupdf-918467458d7572e5d54fc93f60d71c8d80f368f6.tar.xz
support ProfXJC
-rw-r--r--source/pdf/pdf-crypt.c257
1 files changed, 257 insertions, 0 deletions
diff --git a/source/pdf/pdf-crypt.c b/source/pdf/pdf-crypt.c
index 7be666d2..af805469 100644
--- a/source/pdf/pdf-crypt.c
+++ b/source/pdf/pdf-crypt.c
@@ -2,6 +2,131 @@
#include "mupdf/pdf.h"
#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct
+{
+ int n;
+ uint32_t MT[1248];
+ uint32_t cc;
+} mt19937;
+
+static void fcn_40c060(mt19937 *ctx)
+{
+ uint32_t *edi = &ctx->MT[624];
+ uint32_t eax, ecx, edx;
+
+ for (int i = 0; i < 227; i++) {
+ edx = ((edi[i] ^ edi[i + 1]) & 0x7fffffff) ^ edi[i];
+ if (edx & 1) {
+ eax = 0x9908b0df;
+ } else {
+ eax = 0;
+ }
+ edx >>= 1;
+ edi[i - 624] = eax ^ edi[i + 397] ^ edx;
+ }
+
+ uint32_t *esi = &ctx->MT[851];
+
+ for (int i = 0; i < 396; i++) {
+ ecx = ((esi[i] ^ esi[i + 1]) & 0x7fffffff) ^ esi[i];
+
+ if (ecx & 1) {
+ eax = 0x9908b0df;
+ } else {
+ eax = 0;
+ }
+ ecx >>= 1;
+ esi[i - 624] = eax ^ esi[i - 851] ^ ecx;
+ }
+
+ ecx = ((ctx->MT[1247] ^ ctx->MT[0]) & 0x7fffffff) ^ ctx->MT[1247];
+ if (ecx & 1) {
+ eax = 0x9908b0df;
+ } else {
+ eax = 0;
+ }
+ ecx >>= 1;
+ ctx->MT[623] = eax ^ ctx->MT[396] ^ ecx;
+ ctx->n = 0;
+}
+
+static void fcn_40c120(mt19937* ctx)
+{
+ uint32_t *mt = ctx->MT;
+ uint32_t ecx, edx;
+
+ for (int i = 0; i < 624; i++) {
+ edx = ((mt[i] ^ mt[i + 1]) & 0x7fffffff) ^ mt[i];
+
+ if (edx & 1) {
+ ecx = 0x9908b0df;
+ } else {
+ ecx = 0;
+ }
+ edx >>= 1;
+
+ ecx ^= mt[i + 397];
+ ecx ^= edx;
+
+ mt[i + 624] = ecx;
+ }
+}
+
+uint32_t mt19937_next(mt19937 *ctx)
+{
+ int n = ctx->n;
+ uint32_t eax, edx;
+
+ if (n == 624) {
+ fcn_40c120(ctx);
+ } else if (n >= 1248) {
+ fcn_40c060(ctx);
+ }
+ ctx->n++;
+
+ edx = ctx->MT[n] ^ (ctx->cc & (ctx->MT[n] >> 11));
+ eax = (edx & 0xff3a58ad) << 7;
+ edx ^= eax;
+ eax = (edx & 0xffffdf8c) << 15;
+ edx ^= eax;
+ eax = edx >> 18;
+ return eax ^ edx;
+}
+
+void mt19937_init(mt19937 *ctx, uint32_t seed)
+{
+ ctx->n = 624;
+ ctx->cc = ~0;
+
+ ctx->MT[0] = seed;
+ for (int i = 1; i < 624; i++) {
+ ctx->MT[i] = 0x6c078965UL * (ctx->MT[i-1] ^ (ctx->MT[i-1] >> 30)) + i;
+ }
+}
+
+uint32_t hex_to_dword(const char *hex)
+{
+ uint32_t result = 0;
+ for (int i = 0; i < 4; i++) {
+ uint32_t t = 0;
+ for (int j = 0; j < 2; j++) {
+ t *= 16;
+ if (hex[j] >= '0' && hex[j] <= '9')
+ t += hex[j] - '0';
+ else if (hex[j] >= 'A' && hex[j] <= 'F')
+ t += hex[j] - 'A' + 10;
+ else if (hex[j] >= 'a' && hex[j] <= 'f')
+ t += hex[j] - 'a' + 10;
+ }
+ result |= (t << (i * 8));
+ hex += 2;
+ }
+ return result;
+}
enum
{
@@ -39,10 +164,135 @@ struct pdf_crypt_s
int encrypt_metadata;
unsigned char key[32]; /* decryption key generated from password */
+ int xjc;
};
static void pdf_parse_crypt_filter(fz_context *ctx, pdf_crypt_filter *cf, pdf_crypt *crypt, pdf_obj *name);
+static pdf_crypt *
+pdf_set_profxjc_crypt(pdf_crypt *crypt, fz_context *ctx, pdf_obj *dict, pdf_obj *id)
+{
+ fz_sha256 sha256;
+ fz_sha256 xml_hash;
+ unsigned char xml_hash_sum[32];
+ unsigned char *r_buf = NULL;
+ size_t r_size = 0;
+ mt19937 mtctx;
+ unsigned char crypt_bytes[4] = { 3, 0, 0, 0 }; /* AESV3 */
+ unsigned char len_bytes[4] = { 0, 0, 0, 0 };
+ unsigned char idstr[32];
+ pdf_obj *obj;
+
+ crypt->xjc = 1;
+
+ obj = pdf_dict_get(ctx, dict, PDF_NAME(Length));
+ if (pdf_is_int(ctx, obj))
+ crypt->length = pdf_to_int(ctx, obj);
+
+ obj = pdf_dict_get(ctx, dict, PDF_NAME(P));
+ if (pdf_is_int(ctx, obj))
+ crypt->p = pdf_to_int(ctx, obj);
+ else
+ {
+ fz_warn(ctx, "encryption dictionary missing permissions");
+ crypt->p = 0xfffffffc;
+ }
+
+ crypt->stmf.method = PDF_CRYPT_AESV3;
+ crypt->stmf.length = crypt->length;
+ crypt->strf.method = PDF_CRYPT_AESV3;
+ crypt->strf.length = crypt->length;
+ len_bytes[0] = crypt->length / 8;
+
+ fz_sha256_init(&sha256);
+ fz_sha256_update(&sha256, "ProfXJC.General", 15);
+ fz_sha256_update(&sha256, crypt_bytes, 4);
+ fz_sha256_update(&sha256, len_bytes, 4);
+ fz_sha256_update(&sha256, &crypt->p, 4); /* TODO: may not work for big endian */
+
+ if (pdf_is_array(ctx, id) && pdf_array_len(ctx, id) == 2)
+ {
+ const char *buf;
+ size_t osz;
+
+ /* the original id setter */
+ obj = pdf_array_get(ctx, id, 0);
+ if (pdf_is_string(ctx, obj))
+ crypt->id = pdf_keep_obj(ctx, obj);
+
+ /* TODO: what if id is not 16*2 bytes? */
+ obj = pdf_array_get(ctx, id, 0);
+ buf = pdf_to_string(ctx, obj, &osz);
+ if (osz == 16) {
+ memcpy(idstr, buf, 16);
+ } else {
+ fz_warn(ctx, "length of id[0] is %ld", osz);
+ }
+ obj = pdf_array_get(ctx, id, 1);
+ buf = pdf_to_string(ctx, obj, &osz);
+ if (osz == 16) {
+ memcpy(idstr + 16, buf, 16);
+ } else {
+ fz_warn(ctx, "length of id[1] is %ld", osz);
+ }
+ }
+
+ obj = pdf_dict_gets(ctx, dict, "Recipients");
+ if (pdf_is_string(ctx, obj)) {
+ r_size = pdf_to_str_len(ctx, obj);
+
+ r_buf = fz_malloc(ctx, r_size);
+ memcpy(r_buf, pdf_to_str_buf(ctx, obj), r_size);
+ unsigned char tmpbyte = 0xff;
+ int ididx = 0;
+ for (int i = 0; i < r_size; i++) {
+ tmpbyte ^= idstr[ididx];
+ ididx = (ididx + 1) % 32;
+ r_buf[i] ^= tmpbyte;
+ }
+ }
+
+ const char *sseed = strstr(r_buf, "Seed=\"");
+ if (!sseed) {
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot find the seed");
+ }
+ uint32_t seed = strtoll(sseed + 6, NULL, 10);
+ mt19937_init(&mtctx, seed);
+
+ const char *userpass = strstr(r_buf, "<xjc:UserPassword>");
+ if (!userpass) {
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot find UserPassword");
+ }
+ userpass += 18;
+
+ uint32_t tmpword = 0xffffffff;
+ uint32_t pass[0x20];
+ for (int i = 0; i < 0x20; i++) {
+ tmpword ^= mt19937_next(&mtctx);
+ uint32_t dw = hex_to_dword(userpass);
+ userpass += 8;
+ pass[i] = dw ^ tmpword;
+ }
+
+ fz_sha256_update(&sha256, pass, 0x80);
+ fz_sha256_update(&sha256, r_buf, r_size);
+
+ fz_sha256_init(&xml_hash);
+ fz_sha256_update(&xml_hash, r_buf, r_size);
+ fz_sha256_final(&xml_hash, xml_hash_sum);
+
+ fz_sha256_update(&sha256, xml_hash_sum, 32);
+ fz_sha256_final(&sha256, crypt->key);
+
+ for (int i = 0; i < 43; i++) {
+ fz_sha256_init(&sha256);
+ fz_sha256_update(&sha256, crypt->key, 32);
+ fz_sha256_final(&sha256, crypt->key);
+ }
+ fz_free(ctx, r_buf);
+ return crypt;
+}
+
/*
* Create crypt object for decrypting strings and streams
* given the Encryption and ID objects.
@@ -64,6 +314,11 @@ pdf_new_crypt(fz_context *ctx, pdf_obj *dict, pdf_obj *id)
pdf_drop_crypt(ctx, crypt);
fz_throw(ctx, FZ_ERROR_GENERIC, "unspecified encryption handler");
}
+ crypt->xjc = 0;
+ if (!strcmp("ProfXJC", pdf_to_name(ctx, obj)))
+ {
+ return pdf_set_profxjc_crypt(crypt, ctx, dict, id);
+ }
if (!pdf_name_eq(ctx, PDF_NAME(Standard), obj))
{
pdf_drop_crypt(ctx, crypt);
@@ -799,6 +1054,8 @@ pdf_needs_password(fz_context *ctx, pdf_document *doc)
{
if (!doc->crypt)
return 0;
+ if (doc->crypt->xjc)
+ return 0;
if (pdf_authenticate_password(ctx, doc, ""))
return 0;
return 1;