From 918467458d7572e5d54fc93f60d71c8d80f368f6 Mon Sep 17 00:00:00 2001 From: Iru Cai Date: Sun, 18 Nov 2018 23:37:45 +0800 Subject: support ProfXJC --- source/pdf/pdf-crypt.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) 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 +#include +#include +#include + +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, ""); + 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; -- cgit v1.2.3