summaryrefslogtreecommitdiff
path: root/Build/source/libs/pplib/pplib-src/src
diff options
context:
space:
mode:
authorLuigi Scarso <luigi.scarso@gmail.com>2023-05-23 17:09:02 +0000
committerLuigi Scarso <luigi.scarso@gmail.com>2023-05-23 17:09:02 +0000
commit528c3b5af1e5c594dd962277fc8d54e6a2303d07 (patch)
tree14be3aebd130dea3cae991d94119705e8a8383fa /Build/source/libs/pplib/pplib-src/src
parenta2fc32755a1bb6e59bfaaa2e8ec69ada58bd026c (diff)
fixes decryption failure on unreadable perms (Paweł Jackowski)
git-svn-id: svn://tug.org/texlive/trunk@67196 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/pplib/pplib-src/src')
-rw-r--r--Build/source/libs/pplib/pplib-src/src/Makefile.vc3
-rw-r--r--Build/source/libs/pplib/pplib-src/src/ppapi.h2
-rw-r--r--Build/source/libs/pplib/pplib-src/src/ppcrypt.c430
-rw-r--r--Build/source/libs/pplib/pplib-src/src/ppcrypt.h34
-rw-r--r--Build/source/libs/pplib/pplib-src/src/pptest2.c46
5 files changed, 389 insertions, 126 deletions
diff --git a/Build/source/libs/pplib/pplib-src/src/Makefile.vc b/Build/source/libs/pplib/pplib-src/src/Makefile.vc
index 481d9b20ab7..ac0f9d1c6f5 100644
--- a/Build/source/libs/pplib/pplib-src/src/Makefile.vc
+++ b/Build/source/libs/pplib/pplib-src/src/Makefile.vc
@@ -19,9 +19,10 @@ CFLAGS=/W3 /D_CRT_SECURE_NO_DEPRECATE /O2
# 4710: no inlined
# 4711: inline expansion
# 4706: assignment in conditionals
+# 4061: switch(enum) case label missing, even if default is there
#WFLAGS= /W3 /D_CRT_SECURE_NO_DEPRECATE
-WFLAGS= /Wall /D_CRT_SECURE_NO_DEPRECATE /wd5045 /wd4820 /wd4456 /wd4457 /wd4668 /wd4244 /wd4127 /wd4131 /wd4464 /wd4201 /wd4710 /wd4711
+WFLAGS= /Wall /D_CRT_SECURE_NO_DEPRECATE /wd5045 /wd4820 /wd4456 /wd4457 /wd4668 /wd4244 /wd4127 /wd4131 /wd4464 /wd4201 /wd4710 /wd4711 /wd4061
CFLAGS= $(WFLAGS) /O2
#LFLAGS = /NODEFAULTLIB:LIBCMT # produces binary dependent from redistributable
diff --git a/Build/source/libs/pplib/pplib-src/src/ppapi.h b/Build/source/libs/pplib/pplib-src/src/ppapi.h
index 94956f8cb56..56137f8f21f 100644
--- a/Build/source/libs/pplib/pplib-src/src/ppapi.h
+++ b/Build/source/libs/pplib/pplib-src/src/ppapi.h
@@ -8,7 +8,7 @@
#include "ppconf.h"
-#define pplib_version "v2.05 less toxic i hope"
+#define pplib_version "v2.2"
#define pplib_author "p.jackowski@gust.org.pl"
/* types */
diff --git a/Build/source/libs/pplib/pplib-src/src/ppcrypt.c b/Build/source/libs/pplib/pplib-src/src/ppcrypt.c
index 266bacc29b2..832e8c327cb 100644
--- a/Build/source/libs/pplib/pplib-src/src/ppcrypt.c
+++ b/Build/source/libs/pplib/pplib-src/src/ppcrypt.c
@@ -44,30 +44,156 @@ int ppcrypt_type (ppcrypt *crypt, ppname *cryptname, ppuint *length, int *cryptf
return 1;
}
-static const uint8_t padding_string[] = {
+/* V1..4 algorithms */
+
+/* V1..4 unicode do PdfDocEncoding */
+
+typedef struct {
+ uint32_t unicode;
+ uint32_t code;
+ uint32_t count;
+} map_range_t;
+
+static const map_range_t unicode_to_pdf_doc_encoding_map[] = {
+ { 32, 32, 95 },
+ { 161, 161, 12 },
+ { 174, 174, 82 },
+ { 305, 154, 1 },
+ { 321, 149, 1 },
+ { 322, 155, 1 },
+ { 338, 150, 1 },
+ { 339, 156, 1 },
+ { 352, 151, 1 },
+ { 353, 157, 1 },
+ { 376, 152, 1 },
+ { 381, 153, 1 },
+ { 382, 158, 1 },
+ { 402, 134, 1 },
+ { 710, 26, 1 },
+ { 711, 25, 1 },
+ { 728, 24, 1 },
+ { 729, 27, 1 },
+ { 730, 30, 1 },
+ { 731, 29, 1 },
+ { 732, 31, 1 },
+ { 733, 28, 1 },
+ { 8211, 133, 1 },
+ { 8212, 132, 1 },
+ { 8216, 143, 3 },
+ { 8220, 141, 2 },
+ { 8222, 140, 1 },
+ { 8224, 129, 2 },
+ { 8226, 128, 1 },
+ { 8230, 131, 1 },
+ { 8240, 139, 1 },
+ { 8249, 136, 2 },
+ { 8260, 135, 1 },
+ { 8364, 160, 1 },
+ { 8482, 146, 1 },
+ { 8722, 138, 1 },
+ { 64257, 147, 2 }
+};
+
+#define unicode_to_pdf_doc_encoding_entries (sizeof(unicode_to_pdf_doc_encoding_map) / sizeof(map_range_t))
+
+static int unicode_to_pdf_doc_encoding (uint32_t unicode, uint8_t *pcode)
+{
+ const map_range_t *left, *right, *mid;
+
+ left = &unicode_to_pdf_doc_encoding_map[0];
+ right = &unicode_to_pdf_doc_encoding_map[unicode_to_pdf_doc_encoding_entries - 1];
+ for ( ; left <= right; )
+ {
+ mid = left + ((right - left) / 2);
+ if (unicode > mid->unicode + mid->count - 1)
+ left = mid + 1;
+ else if (unicode < mid->unicode)
+ right = mid - 1;
+ else
+ {
+ *pcode = (uint8_t)(mid->code + (unicode - mid->unicode));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#define utf8_unicode2(p) (((p[0]&31)<<6)|(p[1]&63))
+#define utf8_unicode3(p) (((p[0]&15)<<12)|((p[1]&63)<<6)|(p[2]&63))
+#define utf8_unicode4(p) (((p[0]&7)<<18)|((p[1]&63)<<12)|((p[2]&63)<<6)|(p[3]&63))
+
+#define utf8_get1(p, e, unicode) ((unicode = p[0]), p + 1)
+#define utf8_get2(p, e, unicode) (p + 1 < e ? ((unicode = utf8_unicode2(p)), p + 2) : NULL)
+#define utf8_get3(p, e, unicode) (p + 2 < e ? ((unicode = utf8_unicode3(p)), p + 3) : NULL)
+#define utf8_get4(p, e, unicode) (p + 4 < e ? ((unicode = utf8_unicode3(p)), p + 4) : NULL)
+
+#define utf8_get(p, e, unicode) \
+ (p[0] < 0x80 ? utf8_get1(p, e, unicode) : \
+ p[0] < 0xC0 ? NULL : \
+ p[0] < 0xE0 ? utf8_get2(p, e, unicode) : \
+ p[0] < 0xF0 ? utf8_get3(p, e, unicode) : utf8_get4(p, e, unicode))
+
+static int ppcrypt_password_encoding (uint8_t *password, size_t *passwordlength)
+{
+ uint8_t *p, newpassword[PPCRYPT_MAX_PASSWORD], *n;
+ const uint8_t *e;
+ uint32_t unicode = 0;
+
+ for (n = &newpassword[0], p = &password[0], e = p + *passwordlength; p < e; ++n)
+ {
+ p = utf8_get(p, e, unicode);
+ if (p == NULL)
+ return 0;
+ if (unicode_to_pdf_doc_encoding(unicode, n) == 0)
+ return 0;
+ }
+ *passwordlength = n - &newpassword[0];
+ memcpy(password, newpassword, *passwordlength);
+ return 1;
+}
+
+/* setup passwords */
+
+static const uint8_t password_padding[] = {
0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
};
-static void ppcrypt_set_userpass (ppcrypt *crypt, const void *userpass, size_t userpasslength)
+static void ppcrypt_set_user_password (ppcrypt *crypt, const void *userpass, size_t userpasslength)
{
- crypt->userpasslength = userpasslength > 32 ? 32 : userpasslength;
+ crypt->userpasslength = userpasslength > PPCRYPT_MAX_PASSWORD ? PPCRYPT_MAX_PASSWORD : userpasslength;
memcpy(crypt->userpass, userpass, crypt->userpasslength);
- memcpy(crypt->userpass + crypt->userpasslength, padding_string, 32 - crypt->userpasslength);
+ if (crypt->algorithm_variant < 5)
+ {
+ if (ppcrypt_password_encoding(crypt->userpass, &crypt->userpasslength) == 0)
+ return;
+ if (crypt->userpasslength > 32)
+ crypt->userpasslength = 32;
+ else if (crypt->userpasslength < 32)
+ memcpy(&crypt->userpass[crypt->userpasslength], password_padding, 32 - crypt->userpasslength);
+ }
crypt->flags |= PPCRYPT_USER_PASSWORD;
}
-static void ppcrypt_set_ownerpass (ppcrypt *crypt, const void *ownerpass, size_t ownerpasslength)
+static void ppcrypt_set_owner_password (ppcrypt *crypt, const void *ownerpass, size_t ownerpasslength)
{
- crypt->ownerpasslength = ownerpasslength > 32 ? 32 : ownerpasslength;
+ crypt->ownerpasslength = ownerpasslength > PPCRYPT_MAX_PASSWORD ? PPCRYPT_MAX_PASSWORD : ownerpasslength;
memcpy(crypt->ownerpass, ownerpass, crypt->ownerpasslength);
- memcpy(crypt->ownerpass + crypt->ownerpasslength, padding_string, 32 - crypt->ownerpasslength);
+ if (crypt->algorithm_variant < 5)
+ {
+ if (ppcrypt_password_encoding(crypt->ownerpass, &crypt->ownerpasslength) == 0)
+ return;
+ if (crypt->ownerpasslength > 32)
+ crypt->ownerpasslength = 32;
+ else if (crypt->ownerpasslength < 32)
+ memcpy(&crypt->ownerpass[crypt->ownerpasslength], password_padding, 32 - crypt->ownerpasslength);
+ }
crypt->flags |= PPCRYPT_OWNER_PASSWORD;
}
-/* retrieving user password from owner password and owner key (variant < 5) */
+/* V1..4 retrieving user password from owner password and owner key (variant < 5) */
-static void ppcrypt_retrieve_userpass (ppcrypt *crypt, const void *ownerkey, size_t ownerkeysize)
+static void ppcrypt_user_password_from_owner_key (ppcrypt *crypt, const void *ownerkey, size_t ownerkeysize)
{
uint8_t temp[16], rc4key[32], rc4key2[32];
uint8_t i;
@@ -97,14 +223,14 @@ static void ppcrypt_retrieve_userpass (ppcrypt *crypt, const void *ownerkey, siz
}
//crypt->userpasslength = 32;
for (crypt->userpasslength = 0; crypt->userpasslength < 32; ++crypt->userpasslength)
- if (memcmp(&crypt->userpass[crypt->userpasslength], padding_string, 32 - crypt->userpasslength) == 0)
+ if (memcmp(&crypt->userpass[crypt->userpasslength], password_padding, 32 - crypt->userpasslength) == 0)
break;
crypt->flags |= PPCRYPT_USER_PASSWORD;
}
-/* generating file key; pdf spec p. 125 */
+/* V1..4 generating file key; pdf spec p. 125 */
-static void ppcrypt_filekey (ppcrypt *crypt, const void *ownerkey, size_t ownerkeysize, const void *id, size_t idsize)
+static void ppcrypt_compute_file_key (ppcrypt *crypt, const void *ownerkey, size_t ownerkeysize, const void *id, size_t idsize)
{
uint32_t p;
uint8_t permissions[4], temp[16];
@@ -134,9 +260,9 @@ static void ppcrypt_filekey (ppcrypt *crypt, const void *ownerkey, size_t ownerk
}
}
-/* generating userkey for comparison with /U; requires a general file key and id; pdf spec page 126-127 */
+/* V1..4 generating userkey for comparison with /U; requires a general file key and id; pdf spec page 126-127 */
-static void ppcrypt_userkey (ppcrypt *crypt, const void *id, size_t idsize, uint8_t *password_hash)
+static void ppcrypt_compute_user_key (ppcrypt *crypt, const void *id, size_t idsize, uint8_t password_hash[32])
{
uint8_t rc4key2[32];
uint8_t i;
@@ -144,13 +270,13 @@ static void ppcrypt_userkey (ppcrypt *crypt, const void *id, size_t idsize, uint
if (crypt->algorithm_revision <= 2)
{
- rc4_encode_data(padding_string, 32, password_hash, crypt->filekey, crypt->filekeylength);
+ rc4_encode_data(password_padding, 32, password_hash, crypt->filekey, crypt->filekeylength);
}
else
{
md5_state md5;
md5_digest_init(&md5);
- md5_digest_add(&md5, padding_string, 32);
+ md5_digest_add(&md5, password_padding, 32);
md5_digest_add(&md5, id, idsize);
md5_digest_get(&md5, password_hash, MD5_BYTES);
rc4_encode_data(password_hash, 16, password_hash, crypt->filekey, crypt->filekeylength);
@@ -165,25 +291,127 @@ static void ppcrypt_userkey (ppcrypt *crypt, const void *id, size_t idsize, uint
}
}
-/* validating /Perms key (pdf 1.7, /V 5 /R 5 crypt) */
+static ppcrypt_status ppcrypt_authenticate_legacy (ppcrypt *crypt, ppstring *userkey, ppstring *ownerkey, ppstring *id)
+{
+ uint8_t password_hash[32];
+
+ if ((crypt->flags & PPCRYPT_USER_PASSWORD) == 0 && (crypt->flags & PPCRYPT_OWNER_PASSWORD) != 0)
+ ppcrypt_user_password_from_owner_key(crypt, ownerkey, ownerkey->size);
+ ppcrypt_compute_file_key(crypt, ownerkey->data, ownerkey->size, id->data, id->size);
+ ppcrypt_compute_user_key(crypt, id->data, id->size, password_hash); /* needs file key */
+ return memcmp(userkey->data, password_hash, (crypt->algorithm_revision >= 3 ? 16 : 32)) == 0 ? PPCRYPT_DONE : PPCRYPT_PASS;
+}
+
+/* V5 */
static const uint8_t nulliv[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* AES-256 initialization vector */
-static ppcrypt_status ppcrypt_authenticate_perms (ppcrypt *crypt, ppstring *perms)
-{ /* decode /Perms string overriding crypt setup (should match anyway) */
+/* V5 R5..6 password hash */
+
+#define PPCRYPT_MAX_MANGLED ((127+64+48)*64) // 127 password, 64 hash, 48 /U key
+
+static void ppcrypt_password_hash_indeed (const uint8_t *password, size_t passwordlength, const uint8_t *userkey, uint8_t hash[64])
+{
+ size_t hashlength, datalength;
+ uint8_t data[PPCRYPT_MAX_MANGLED], *pdata;
+ uint8_t *key, *iv;
+ uint8_t round, i;
+ uint32_t div3;
+
+ hashlength = 32; /* initial hash is sha256 */
+ round = 0;
+ do
+ {
+ /* concat password, hash, and /U value 64 times */
+ pdata = &data[0];
+ memcpy(pdata, password, passwordlength);
+ pdata += passwordlength;
+ memcpy(pdata, hash, hashlength);
+ pdata += hashlength;
+ if (userkey != NULL)
+ {
+ memcpy(pdata, userkey, 48);
+ pdata += 48;
+ }
+ datalength = pdata - &data[0];
+ for (i = 1; i < 64; ++i, pdata += datalength)
+ memcpy(pdata, &data[0], datalength);
+ datalength *= 64;
+
+ /* encrypt the data with aes128 using hash bytes 1..16 as key and bytes 17..32 as initialization vector
+ encryption inplace, CBC, no-padding, no change to datalength */
+ key = &hash[0]; iv = &hash[16];
+ aes_encode_data(data, datalength, data, key, 16, iv, AES_NULL_PADDING);
+
+ /* get modulo 3 of first 16 bytes number of encrypted data (sum of digits modulo 3) */
+ for (i = 0, div3 = 0; i < 16; ++i)
+ div3 += data[i];
+
+ /* compute new hash using sha256/384/512 */
+ switch (div3 % 3)
+ {
+ case 0:
+ sha256_digest(data, datalength, hash, SHA_BYTES);
+ hashlength = 32;
+ break;
+ case 1:
+ sha384_digest(data, datalength, hash, SHA_BYTES);
+ hashlength = 48;
+ break;
+ case 2:
+ sha512_digest(data, datalength, hash, SHA_BYTES);
+ hashlength = 64;
+ break;
+ }
+
+ /* do 64 times, then keep going until the last byte of data <= round - 32 */
+ } while (++round < 64 || round < data[datalength - 1] + 32);
+
+}
+
+static void ppcrypt_password_hash (ppcrypt *crypt, const uint8_t *password, size_t passwordlength, const uint8_t *salt, const uint8_t *userkey, uint8_t password_hash[32])
+{
+ sha256_state sha;
+ uint8_t hash[64]; /* result password_hash is 32 bytes, but we need 64 for R6 procedure */
+
+ /* take sha256 of password, salt and /U */
+ sha256_digest_init(&sha);
+ sha256_digest_add(&sha, password, passwordlength);
+ sha256_digest_add(&sha, salt, 8);
+ if (userkey != NULL)
+ sha256_digest_add(&sha, userkey, 48);
+ sha256_digest_get(&sha, hash, SHA_BYTES);
+
+ /* V5 R5 - password_hash is the digest, V5 R6 - password_hash is mangled */
+ if (crypt->algorithm_revision >= 6)
+ ppcrypt_password_hash_indeed(password, passwordlength, userkey, hash);
+
+ memcpy(password_hash, hash, 32);
+}
+
+/* V5 permissions */
+
+static ppcrypt_status ppcrypt_authenticate_permissions (ppcrypt *crypt, ppstring *perms)
+{
uint8_t permsdata[16];
- //int64_t p;
- //int i;
aes_decode_data(perms->data, perms->size, permsdata, crypt->filekey, crypt->filekeylength, nulliv, AES_NULL_PADDING);
if (permsdata[9] != 'a' || permsdata[10] != 'd' || permsdata[11] != 'b')
- return PPCRYPT_FAIL;
+ { /* if we get here, the password hash is correct, we don't need to fail because of unreadable perms (found such docs) */
+ crypt->flags |= PPCRYPT_UNREADABLE_PERMISSIONS;
+ return PPCRYPT_DONE;
+ }
- // do not update permissions flags; they seem to be different inside crypt string
- //for (p = 0, i = 0; i < 8; ++i)
- // p = p + (permsdata[i] << (i << 3)); /* low order bytes first */
- //crypt->permissions = (ppint)(int32_t)(p & 0x00000000FFFFFFFFLL); /* unset bits 33..64, treat as 32-bit signed int */
+ /* do not check/update permissions flags here; they might be different inside crypt string */
+ if (0)
+ {
+ int64_t p;
+ int i;
+ for (p = 0, i = 0; i < 8; ++i)
+ p = p + (permsdata[i] << (i << 3)); /* low order bytes first */
+ crypt->permissions = (ppint)((int32_t)(p & 0x00000000FFFFFFFFLL)); /* unset bits 33..64, treat as 32-bit signed int */
+ }
if (permsdata[8] == 'T')
crypt->flags &= ~PPCRYPT_NO_METADATA;
@@ -193,6 +421,61 @@ static ppcrypt_status ppcrypt_authenticate_perms (ppcrypt *crypt, ppstring *perm
return PPCRYPT_DONE;
}
+/* V5 authentication */
+
+static ppcrypt_status ppcrypt_authenticate_user (ppcrypt *crypt, ppstring *u, ppstring *ue, ppstring *perms)
+{
+ uint8_t password_hash[32], *salt;
+
+ salt = (uint8_t *)&u->data[32]; /* validation salt */
+ ppcrypt_password_hash(crypt, crypt->userpass, crypt->userpasslength, salt, NULL, password_hash);
+ if (memcmp(u->data, password_hash, 32) != 0)
+ return PPCRYPT_PASS;
+
+ salt = (uint8_t *)&u->data[40]; /* key salt */
+ ppcrypt_password_hash(crypt, crypt->userpass, crypt->userpasslength, salt, NULL, password_hash);
+ aes_decode_data(ue->data, 32, crypt->filekey, password_hash, 32, nulliv, AES_NULL_PADDING);
+
+ return ppcrypt_authenticate_permissions(crypt, perms);
+}
+
+static ppcrypt_status ppcrypt_authenticate_owner (ppcrypt *crypt, ppstring *u, ppstring *o, ppstring *oe, ppstring *perms)
+{
+ uint8_t password_hash[32], *salt;
+
+ salt = (uint8_t *)&o->data[32]; /* validation salt */
+ ppcrypt_password_hash(crypt, crypt->ownerpass, crypt->ownerpasslength, salt, (uint8_t *)u->data, password_hash);
+ if (memcmp(o->data, password_hash, 32) != 0)
+ return PPCRYPT_PASS;
+
+ salt = (uint8_t *)&o->data[40]; /* key salt */
+ ppcrypt_password_hash(crypt, crypt->ownerpass, crypt->ownerpasslength, salt, (uint8_t *)u->data, password_hash);
+ aes_decode_data(oe->data, 32, crypt->filekey, password_hash, 32, nulliv, AES_NULL_PADDING);
+
+ return ppcrypt_authenticate_permissions(crypt, perms);
+}
+
+
+/* authentication */
+
+static ppcrypt_status ppcrypt_authenticate (ppcrypt *crypt, ppstring *u, ppstring *ue, ppstring *o, ppstring *oe, ppstring *id, ppstring *perms)
+{
+ /* V1..V4 */
+ if (crypt->algorithm_variant < 5)
+ return ppcrypt_authenticate_legacy(crypt, u, o, id);
+
+ /* V5 */
+ if (crypt->flags & PPCRYPT_USER_PASSWORD)
+ if (ppcrypt_authenticate_user(crypt, u, ue, perms) == PPCRYPT_DONE)
+ return PPCRYPT_DONE;
+ if (crypt->flags & PPCRYPT_OWNER_PASSWORD)
+ return ppcrypt_authenticate_owner(crypt, u, o, oe, perms);
+
+ return PPCRYPT_PASS;
+}
+
+/**/
+
ppcrypt_status ppdoc_crypt_init (ppdoc *pdf, const void *userpass, size_t userpasslength, const void *ownerpass, size_t ownerpasslength)
{
ppcrypt *crypt;
@@ -206,15 +489,11 @@ ppcrypt_status ppdoc_crypt_init (ppdoc *pdf, const void *userpass, size_t userpa
int cryptflags, encryptmd;
size_t strkeylength, stmkeylength;
- uint8_t password_hash[32]; /* /U and /O are 48 bytes strings for AES-256, but here we use only 32 */
- uint8_t *validation_salt, *key_salt;
-
- /* Every xref could theoretically have a separate encryption info. Not clarified in pdf spec but it seems that the top
- level xref encryption info is the one to be applied to all objects in all xrefs, including older. */
trailer = ppxref_trailer(pdf->xref);
if ((obj = ppdict_get_obj(trailer, "Encrypt")) == NULL)
return PPCRYPT_NONE;
- /* Typically this is all done early, before loading body, so if /Encrypt is indirect reference, it points nothing. We have to load it here. */
+
+ /* this happens early, before loading body, so if /Encrypt is indirect reference, it points nothing */
obj = ppobj_preloaded(pdf, obj);
if (obj->type != PPDICT)
return PPCRYPT_FAIL;
@@ -227,20 +506,23 @@ ppcrypt_status ppdoc_crypt_init (ppdoc *pdf, const void *userpass, size_t userpa
if ((crypt = pdf->crypt) == NULL)
crypt = pdf->crypt = ppcrypt_create(&pdf->heap);
+
+ /* get /V /R /P */
if (!ppdict_get_uint(encrypt, "V", &crypt->algorithm_variant))
crypt->algorithm_variant = 0;
if (crypt->algorithm_variant < 1 || crypt->algorithm_variant > 5)
return PPCRYPT_FAIL;
if (!ppdict_get_uint(encrypt, "R", &crypt->algorithm_revision))
return PPCRYPT_FAIL;
- if (crypt->algorithm_revision >= 3)
- crypt->flags |= PPCRYPT_OBSCURITY;
if (!ppdict_get_int(encrypt, "P", &crypt->permissions))
return PPCRYPT_FAIL;
+
+ /* get /O /U /ID /OE /UE */
if ((userkey = ppdict_get_string(encrypt, "U")) == NULL || (ownerkey = ppdict_get_string(encrypt, "O")) == NULL)
return PPCRYPT_FAIL;
userkey = ppstring_decoded(userkey);
ownerkey = ppstring_decoded(ownerkey);
+
/* for some reason acrobat pads /O and /U to 127 bytes with NULL, so we don't check the exact length but ensure the minimal */
hashlength = crypt->algorithm_variant < 5 ? 32 : 48;
if (userkey->size < hashlength || ownerkey->size < hashlength)
@@ -266,6 +548,7 @@ ppcrypt_status ppdoc_crypt_init (ppdoc *pdf, const void *userpass, size_t userpa
return PPCRYPT_FAIL;
}
+ /* collect flags and keylength */
switch (crypt->algorithm_revision)
{
case 1:
@@ -279,7 +562,7 @@ ppcrypt_status ppdoc_crypt_init (ppdoc *pdf, const void *userpass, size_t userpa
crypt->filekeylength = 5; /* 40 bits, 5 bytes */
crypt->flags |= PPCRYPT_RC4;
break;
- case 4: case 5:
+ case 4: case 5: case 6:
if ((crypt->map = ppdict_rget_dict(encrypt, "CF")) == NULL)
return PPCRYPT_FAIL;
for (ppdict_first(crypt->map, pkey, obj); *pkey != NULL; ppdict_next(pkey, obj))
@@ -327,72 +610,15 @@ ppcrypt_status ppdoc_crypt_init (ppdoc *pdf, const void *userpass, size_t userpa
return PPCRYPT_FAIL;
}
- /* password */
-
+ /* setup passwords */
if (userpass != NULL)
- {
- ppcrypt_set_userpass(crypt, userpass, userpasslength);
- }
- else if (ownerpass != NULL)
- {
- if (crypt->algorithm_variant < 5) // fetch user password from owner password
- ppcrypt_retrieve_userpass(crypt, ownerkey, ppstring_size(ownerkey));
- else // open the document using owner password
- ppcrypt_set_ownerpass(crypt, ownerpass, ownerpasslength);
- }
- else
- {
- return PPCRYPT_FAIL;
- }
-
- if (crypt->algorithm_variant < 5)
- { /* authenticate by comparing a generated vs present /U entry; depending on variant 16 or 32 bytes to compare */
- ppcrypt_filekey(crypt, ownerkey->data, ownerkey->size, id->data, id->size);
- ppcrypt_userkey(crypt, id->data, id->size, password_hash); /* needs file key so comes after key generation */
- if (memcmp(userkey->data, password_hash, (crypt->algorithm_revision >= 3 ? 16 : 32)) == 0)
- return PPCRYPT_DONE;
+ ppcrypt_set_user_password(crypt, userpass, userpasslength);
+ if (ownerpass != NULL)
+ ppcrypt_set_owner_password(crypt, ownerpass, ownerpasslength);
+ if ((crypt->flags & (PPCRYPT_USER_PASSWORD|PPCRYPT_OWNER_PASSWORD)) == 0)
return PPCRYPT_PASS;
- }
- if (crypt->flags & PPCRYPT_USER_PASSWORD)
- {
- sha256_state sha;
- validation_salt = (uint8_t *)userkey + 32;
- key_salt = validation_salt + 8;
- sha256_digest_init(&sha);
- sha256_digest_add(&sha, crypt->userpass, crypt->userpasslength);
- sha256_digest_add(&sha, validation_salt, 8);
- sha256_digest_get(&sha, password_hash, SHA_BYTES);
- if (memcmp(userkey->data, password_hash, 32) != 0)
- return PPCRYPT_PASS;
- sha256_digest_init(&sha);
- sha256_digest_add(&sha, crypt->userpass, crypt->userpasslength);
- sha256_digest_add(&sha, key_salt, 8);
- sha256_digest_get(&sha, password_hash, SHA_BYTES);
- aes_decode_data(userkey_e->data, 32, crypt->filekey, password_hash, 32, nulliv, AES_NULL_PADDING);
- return ppcrypt_authenticate_perms(crypt, perms);
- }
- if (crypt->flags & PPCRYPT_OWNER_PASSWORD)
- {
- sha256_state sha;
- validation_salt = (uint8_t *)ownerkey + 32;
- key_salt = validation_salt + 8;
- sha256_digest_init(&sha);
- sha256_digest_add(&sha, crypt->ownerpass, crypt->ownerpasslength);
- sha256_digest_add(&sha, validation_salt, 8);
- sha256_digest_add(&sha, userkey, 48);
- sha256_digest_get(&sha, password_hash, SHA_BYTES);
- if (memcmp(ownerkey->data, password_hash, 32) != 0)
- return PPCRYPT_PASS;
- sha256_digest_init(&sha);
- sha256_digest_add(&sha, crypt->ownerpass, crypt->ownerpasslength);
- sha256_digest_add(&sha, key_salt, 8);
- sha256_digest_add(&sha, userkey, 48);
- sha256_digest_get(&sha, password_hash, SHA_BYTES);
- aes_decode_data(ownerkey_e->data, 32, crypt->filekey, password_hash, 32, nulliv, AES_NULL_PADDING);
- return ppcrypt_authenticate_perms(crypt, perms);
- }
- return PPCRYPT_FAIL; // should never get here
+ return ppcrypt_authenticate(crypt, userkey, userkey_e, ownerkey, ownerkey_e, id, perms);
}
/* decrypting strings */
@@ -429,10 +655,10 @@ static void ppcrypt_strkey (ppcrypt *crypt, ppref *ref, int aes)
if (aes)
{
- crypt->filekey[crypt->filekeylength + 5] = 0x73;
- crypt->filekey[crypt->filekeylength + 6] = 0x41;
- crypt->filekey[crypt->filekeylength + 7] = 0x6C;
- crypt->filekey[crypt->filekeylength + 8] = 0x54;
+ crypt->filekey[crypt->filekeylength + 5] = 0x73; // s
+ crypt->filekey[crypt->filekeylength + 6] = 0x41; // A
+ crypt->filekey[crypt->filekeylength + 7] = 0x6C; // l
+ crypt->filekey[crypt->filekeylength + 8] = 0x54; // T
}
md5_digest(crypt->filekey, crypt->filekeylength + (aes ? 9 : 5), crypt->cryptkey, MD5_BYTES);
diff --git a/Build/source/libs/pplib/pplib-src/src/ppcrypt.h b/Build/source/libs/pplib/pplib-src/src/ppcrypt.h
index 98864f860c7..a7131adbb31 100644
--- a/Build/source/libs/pplib/pplib-src/src/ppcrypt.h
+++ b/Build/source/libs/pplib/pplib-src/src/ppcrypt.h
@@ -6,21 +6,25 @@
#include "utilcrypt.h"
#include "utilcryptdef.h"
+#define PPCRYPT_MAX_PASSWORD 127
+#define PPCRYPT_MAX_KEY 32
+
typedef struct {
- ppuint algorithm_variant; /* /V entry of encrypt dict */
- ppuint algorithm_revision; /* /R entry of encrypt dict */
- ppint permissions; /* /P entry of encrypt dict */
- ppdict *map; /* /CF filters map of encrypt dict */
- uint8_t userpass[32]; /* padded user password */
- size_t userpasslength; /* the length of unpadded user password */
- uint8_t ownerpass[32]; /* padded owner password */
- size_t ownerpasslength; /* the length of unpadded owner password */
- uint8_t filekey[32+5+4]; /* generated file key with extra space of 5..9 bytes for salt */
- size_t filekeylength; /* key length; usually 5, 16 or 32 bytes */
- uint8_t cryptkey[32]; /* final crypt key for a given reference */
- size_t cryptkeylength; /* final crypt key length; usually keylength + 5 */
- ppref *ref; /* currently loaded ref (each ref may have a different key) */
- union { /* cached crypt states for strings encrypted/decrypted with the same key */
+ ppuint algorithm_variant; /* /V entry of encrypt dict */
+ ppuint algorithm_revision; /* /R entry of encrypt dict */
+ ppint permissions; /* /P entry of encrypt dict */
+ ppdict *map; /* /CF filters map of encrypt dict */
+ uint8_t userpass[PPCRYPT_MAX_PASSWORD]; /* user password */
+ size_t userpasslength; /* user password length */
+ uint8_t ownerpass[PPCRYPT_MAX_PASSWORD]; /* owner password */
+ size_t ownerpasslength; /* owner password length */
+ uint8_t filekey[PPCRYPT_MAX_KEY+5+4]; /* file key with an extra space for salt */
+ size_t filekeylength; /* key length; usually 5, 16 or 32 bytes */
+ uint8_t cryptkey[PPCRYPT_MAX_KEY]; /* crypt key for a recent reference */
+ size_t cryptkeylength; /* crypt key length; usually keylength + 5 */
+ //ppstring *cryptkeystring; /* todo: cached cryptkey string for V5, where all refs has the same */
+ ppref *ref; /* recent reference */
+ union { /* cached crypt states for strings encrypted/decrypted with the same key */
struct {
rc4_state rc4state;
rc4_map rc4map;
@@ -42,7 +46,7 @@ typedef struct {
#define PPCRYPT_STRING_RC4 (1<<4)
#define PPCRYPT_STREAM_AES (1<<5)
#define PPCRYPT_STRING_AES (1<<6)
-#define PPCRYPT_OBSCURITY (1<<7)
+#define PPCRYPT_UNREADABLE_PERMISSIONS (1<<7)
#define PPCRYPT_STREAM (PPCRYPT_STREAM_AES|PPCRYPT_STREAM_RC4)
#define PPCRYPT_STRING (PPCRYPT_STRING_AES|PPCRYPT_STRING_RC4)
diff --git a/Build/source/libs/pplib/pplib-src/src/pptest2.c b/Build/source/libs/pplib/pplib-src/src/pptest2.c
index 766110da4cd..5dff63afda1 100644
--- a/Build/source/libs/pplib/pplib-src/src/pptest2.c
+++ b/Build/source/libs/pplib/pplib-src/src/pptest2.c
@@ -28,6 +28,9 @@ static int usage (const char *argv0)
{
printf("pplib " pplib_version ", " pplib_author "\n");
printf("usage: %s file1.pdf file2.pdf ...\n", argv0);
+ printf(" %s file.pdf -u userpassword\n", argv0);
+ printf(" %s file.pdf -o ownerpassword\n", argv0);
+ printf(" %s file.pdf -p bothpasswords\n", argv0);
return 0;
}
@@ -36,11 +39,27 @@ static void log_callback (const char *message, void *alien)
fprintf((FILE *)alien, "\nooops: %s\n", message);
}
+static const char * get_next_argument (const char *opt, int *a, int argc, const char **argv)
+{
+ const char *next;
+ if ((*a) + 2 < argc)
+ {
+ next = argv[*a + 1];
+ if (strcmp(next, opt) == 0)
+ {
+ *a += 2;
+ return argv[*a];
+ }
+ }
+ return NULL;
+}
+
int main (int argc, const char **argv)
{
- const char *filepath;
+ const char *filepath, *password;
int a;
ppdoc *pdf;
+ ppcrypt_status cryptstatus;
ppref *pageref;
ppdict *pagedict;
int pageno;
@@ -61,6 +80,7 @@ int main (int argc, const char **argv)
context = ppcontext_new();
for (a = 1; a < argc; ++a)
{
+ /* load */
filepath = argv[a];
printf("loading %s... ", filepath);
pdf = ppdoc_load(filepath);
@@ -69,23 +89,35 @@ int main (int argc, const char **argv)
printf("failed\n");
continue;
}
- printf("done.\n");
- switch (ppdoc_crypt_status(pdf))
+ printf("done\n");
+
+ /* decrypt */
+ if ((password = get_next_argument("-u", &a, argc, argv)) != NULL)
+ cryptstatus = ppdoc_crypt_pass(pdf, password, strlen(password), NULL, 0);
+ else if ((password = get_next_argument("-o", &a, argc, argv)) != NULL)
+ cryptstatus = ppdoc_crypt_pass(pdf, NULL, 0, password, strlen(password));
+ else if ((password = get_next_argument("-p", &a, argc, argv)) != NULL)
+ cryptstatus = ppdoc_crypt_pass(pdf, password, strlen(password), password, strlen(password));
+ else
+ cryptstatus = ppdoc_crypt_status(pdf);
+ switch (cryptstatus)
{
case PPCRYPT_NONE:
+ break;
case PPCRYPT_DONE:
+ printf("opened with password '%s'\n", password != NULL ? password : "");
break;
case PPCRYPT_PASS:
- if (ppdoc_crypt_pass(pdf, "dummy", 5, NULL, 0) == PPCRYPT_DONE || ppdoc_crypt_pass(pdf, NULL, 0, "dummy", 5) == PPCRYPT_DONE)
- break;
- printf("sorry, password needed\n");
+ printf("invalid password\n");
ppdoc_free(pdf);
continue;
case PPCRYPT_FAIL:
- printf("sorry, encryption failed\n");
+ printf("invalid encryption\n");
ppdoc_free(pdf);
continue;
}
+
+ /* process */
sprintf(outname, "%s.out", filepath);
fh = fopen(outname, "wb");
if (fh == NULL)