[poppler] Branch 'xpdf303merge' - poppler/Decrypt.cc poppler/Decrypt.h poppler/PDFDoc.cc poppler/SecurityHandler.cc poppler/SecurityHandler.h poppler/Stream.h

Albert Astals Cid aacid at kemper.freedesktop.org
Tue Aug 30 09:24:28 PDT 2011


 poppler/Decrypt.cc         |  488 +++++++++++++++++++++++++++++++++++++++------
 poppler/Decrypt.h          |   19 +
 poppler/PDFDoc.cc          |    2 
 poppler/SecurityHandler.cc |  238 ++++++++++++---------
 poppler/SecurityHandler.h  |    7 
 poppler/Stream.h           |    3 
 6 files changed, 595 insertions(+), 162 deletions(-)

New commits:
commit 6364c50ffb4053cc30cecbefff7a3142cab8c50b
Author: Albert Astals Cid <aacid at kde.org>
Date:   Tue Aug 30 18:27:09 2011 +0200

    xpdf303: Support for aes256 et all in Decrypt/SecurityHandler

diff --git a/poppler/Decrypt.cc b/poppler/Decrypt.cc
index 2373a66..4042eba 100644
--- a/poppler/Decrypt.cc
+++ b/poppler/Decrypt.cc
@@ -34,11 +34,13 @@
 #include "Decrypt.h"
 #include "Error.h"
 
-static void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
-static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
 static void aesKeyExpansion(DecryptAESState *s,
 			    Guchar *objKey, int objKeyLen);
 static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);
+static void aes256KeyExpansion(DecryptAES256State *s,
+			       Guchar *objKey, int objKeyLen);
+static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last);
+static void sha256(Guchar *msg, int msgLen, Guchar *hash);
 
 static const Guchar passwordPad[32] = {
   0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
@@ -53,67 +55,139 @@ static const Guchar passwordPad[32] = {
 
 GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
 			   GooString *ownerKey, GooString *userKey,
+			   GooString *ownerEnc, GooString *userEnc,
 			   int permissions, GooString *fileID,
 			   GooString *ownerPassword, GooString *userPassword,
 			   Guchar *fileKey, GBool encryptMetadata,
 			   GBool *ownerPasswordOk) {
-  Guchar test[32], test2[32];
+  DecryptAES256State state;
+  Guchar test[127 + 56], test2[32];
   GooString *userPassword2;
   Guchar fState[256];
   Guchar tmpKey[16];
   Guchar fx, fy;
   int len, i, j;
 
-  // try using the supplied owner password to generate the user password
   *ownerPasswordOk = gFalse;
-  if (ownerPassword) {
-    len = ownerPassword->getLength();
-    if (len < 32) {
+
+  if (encRevision == 5) {
+
+    // check the owner password
+    if (ownerPassword) {
+      //~ this is supposed to convert the password to UTF-8 using "SASLprep"
+      len = ownerPassword->getLength();
+      if (len > 127) {
+	len = 127;
+      }
       memcpy(test, ownerPassword->getCString(), len);
-      memcpy(test + len, passwordPad, 32 - len);
-    } else {
-      memcpy(test, ownerPassword->getCString(), 32);
+      memcpy(test + len, ownerKey->getCString() + 32, 8);
+      memcpy(test + len + 8, userKey->getCString(), 48);
+      sha256(test, len + 56, test);
+      if (!memcmp(test, ownerKey->getCString(), 32)) {
+
+	// compute the file key from the owner password
+	memcpy(test, ownerPassword->getCString(), len);
+	memcpy(test + len, ownerKey->getCString() + 40, 8);
+	memcpy(test + len + 8, userKey->getCString(), 48);
+	sha256(test, len + 56, test);
+	aes256KeyExpansion(&state, test, 32);
+	for (i = 0; i < 16; ++i) {
+	  state.cbc[i] = 0;
+	}
+	aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString(), gFalse);
+	memcpy(fileKey, state.buf, 16);
+	aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString() + 16,
+			   gFalse);
+	memcpy(fileKey + 16, state.buf, 16);
+
+	*ownerPasswordOk = gTrue;
+	return gTrue;
+      }
     }
-    md5(test, 32, test);
-    if (encRevision == 3) {
-      for (i = 0; i < 50; ++i) {
-	md5(test, keyLength, test);
+
+    // check the user password
+    if (userPassword) {
+      //~ this is supposed to convert the password to UTF-8 using "SASLprep"
+      len = userPassword->getLength();
+      if (len > 127) {
+	len = 127;
+      }
+      memcpy(test, userPassword->getCString(), len);
+      memcpy(test + len, userKey->getCString() + 32, 8);
+      sha256(test, len + 8, test);
+      if (!memcmp(test, userKey->getCString(), 32)) {
+
+	// compute the file key from the user password
+	memcpy(test, userPassword->getCString(), len);
+	memcpy(test + len, userKey->getCString() + 40, 8);
+	sha256(test, len + 8, test);
+	aes256KeyExpansion(&state, test, 32);
+	for (i = 0; i < 16; ++i) {
+	  state.cbc[i] = 0;
+	}
+	aes256DecryptBlock(&state, (Guchar *)userEnc->getCString(), gFalse);
+	memcpy(fileKey, state.buf, 16);
+	aes256DecryptBlock(&state, (Guchar *)userEnc->getCString() + 16,
+			   gFalse);
+	memcpy(fileKey + 16, state.buf, 16);
+
+	return gTrue; 
       }
     }
-    if (encRevision == 2) {
-      rc4InitKey(test, keyLength, fState);
-      fx = fy = 0;
-      for (i = 0; i < 32; ++i) {
-	test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
+
+    return gFalse;
+  } else {
+
+    // try using the supplied owner password to generate the user password
+    if (ownerPassword) {
+      len = ownerPassword->getLength();
+      if (len < 32) {
+	memcpy(test, ownerPassword->getCString(), len);
+	memcpy(test + len, passwordPad, 32 - len);
+      } else {
+	memcpy(test, ownerPassword->getCString(), 32);
       }
-    } else {
-      memcpy(test2, ownerKey->getCString(), 32);
-      for (i = 19; i >= 0; --i) {
-	for (j = 0; j < keyLength; ++j) {
-	  tmpKey[j] = test[j] ^ i;
+      md5(test, 32, test);
+      if (encRevision == 3) {
+	for (i = 0; i < 50; ++i) {
+	  md5(test, keyLength, test);
 	}
-	rc4InitKey(tmpKey, keyLength, fState);
+      }
+      if (encRevision == 2) {
+	rc4InitKey(test, keyLength, fState);
 	fx = fy = 0;
-	for (j = 0; j < 32; ++j) {
-	  test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
+	for (i = 0; i < 32; ++i) {
+	  test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
+	}
+      } else {
+	memcpy(test2, ownerKey->getCString(), 32);
+	for (i = 19; i >= 0; --i) {
+	  for (j = 0; j < keyLength; ++j) {
+	    tmpKey[j] = test[j] ^ i;
+	  }
+	  rc4InitKey(tmpKey, keyLength, fState);
+	  fx = fy = 0;
+	  for (j = 0; j < 32; ++j) {
+	    test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
+	  }
 	}
       }
-    }
-    userPassword2 = new GooString((char *)test2, 32);
-    if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
-		     permissions, fileID, userPassword2, fileKey,
-		     encryptMetadata)) {
-      *ownerPasswordOk = gTrue;
+      userPassword2 = new GooString((char *)test2, 32);
+      if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+		       permissions, fileID, userPassword2, fileKey,
+		       encryptMetadata)) {
+	*ownerPasswordOk = gTrue;
+	delete userPassword2;
+	return gTrue;
+      }
       delete userPassword2;
-      return gTrue;
     }
-    delete userPassword2;
-  }
 
-  // try using the supplied user password
-  return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
-		      permissions, fileID, userPassword, fileKey,
-		      encryptMetadata);
+    // try using the supplied user password
+    return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+			permissions, fileID, userPassword, fileKey,
+			encryptMetadata);
+  }
 }
 
 GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
@@ -203,7 +277,7 @@ DecryptStream::DecryptStream(Stream *strA, Guchar *fileKey,
 			     int objNum, int objGen):
   FilterStream(strA)
 {
-  int n, i;
+  int i;
 
   algo = algoA;
 
@@ -211,23 +285,36 @@ DecryptStream::DecryptStream(Stream *strA, Guchar *fileKey,
   for (i = 0; i < keyLength; ++i) {
     objKey[i] = fileKey[i];
   }
-  objKey[keyLength] = objNum & 0xff;
-  objKey[keyLength + 1] = (objNum >> 8) & 0xff;
-  objKey[keyLength + 2] = (objNum >> 16) & 0xff;
-  objKey[keyLength + 3] = objGen & 0xff;
-  objKey[keyLength + 4] = (objGen >> 8) & 0xff;
-  if (algo == cryptAES) {
+  switch (algo) {
+  case cryptRC4:
+    objKey[keyLength] = objNum & 0xff;
+    objKey[keyLength + 1] = (objNum >> 8) & 0xff;
+    objKey[keyLength + 2] = (objNum >> 16) & 0xff;
+    objKey[keyLength + 3] = objGen & 0xff;
+    objKey[keyLength + 4] = (objGen >> 8) & 0xff;
+    md5(objKey, keyLength + 5, objKey);
+    if ((objKeyLength = keyLength + 5) > 16) {
+      objKeyLength = 16;
+    }
+    break;
+  case cryptAES:
+    objKey[keyLength] = objNum & 0xff;
+    objKey[keyLength + 1] = (objNum >> 8) & 0xff;
+    objKey[keyLength + 2] = (objNum >> 16) & 0xff;
+    objKey[keyLength + 3] = objGen & 0xff;
+    objKey[keyLength + 4] = (objGen >> 8) & 0xff;
     objKey[keyLength + 5] = 0x73; // 's'
     objKey[keyLength + 6] = 0x41; // 'A'
     objKey[keyLength + 7] = 0x6c; // 'l'
     objKey[keyLength + 8] = 0x54; // 'T'
-    n = keyLength + 9;
-  } else {
-    n = keyLength + 5;
-  }
-  Decrypt::md5(objKey, n, objKey);
-  if ((objKeyLength = keyLength + 5) > 16) {
-    objKeyLength = 16;
+    md5(objKey, keyLength + 9, objKey);
+    if ((objKeyLength = keyLength + 5) > 16) {
+      objKeyLength = 16;
+    }
+    break;
+  case cryptAES256:
+    objKeyLength = keyLength;
+    break;
   }
 
   charactersRead = 0;
@@ -255,6 +342,13 @@ void DecryptStream::reset() {
     }
     state.aes.bufIdx = 16;
     break;
+  case cryptAES256:
+    aes256KeyExpansion(&state.aes256, objKey, objKeyLength);
+    for (i = 0; i < 16; ++i) {
+      state.aes256.cbc[i] = str->getChar();
+    }
+    state.aes256.bufIdx = 16;
+    break;
   }
 }
 
@@ -295,6 +389,22 @@ int DecryptStream::getChar() {
       c = state.aes.buf[state.aes.bufIdx++];
     }
     break;
+  case cryptAES256:
+    if (state.aes256.bufIdx == 16) {
+      for (i = 0; i < 16; ++i) {
+	if ((c = str->getChar()) == EOF) {
+	  return EOF;
+	}
+	in[i] = (Guchar)c;
+      }
+      aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
+    }
+    if (state.aes256.bufIdx == 16) {
+      c = EOF;
+    } else {
+      c = state.aes256.buf[state.aes256.bufIdx++];
+    }
+    break;
   }
   if (c != EOF)
     charactersRead++;
@@ -333,6 +443,22 @@ int DecryptStream::lookChar() {
       c = state.aes.buf[state.aes.bufIdx];
     }
     break;
+  case cryptAES256:
+    if (state.aes256.bufIdx == 16) {
+      for (i = 0; i < 16; ++i) {
+	if ((c = str->getChar()) == EOF) {
+	  return EOF;
+	}
+	in[i] = c;
+      }
+      aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
+    }
+    if (state.aes256.bufIdx == 16) {
+      c = EOF;
+    } else {
+      c = state.aes256.buf[state.aes256.bufIdx];
+    }
+    break;
   }
   return c;
 }
@@ -345,7 +471,7 @@ GBool DecryptStream::isBinary(GBool last) {
 // RC4-compatible decryption
 //------------------------------------------------------------------------
 
-static void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
+void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
   Guchar index1, index2;
   Guchar t;
   int i;
@@ -366,7 +492,7 @@ static void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
   }
 }
 
-static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {
+Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {
   Guchar x1, y1, tx, ty;
 
   x1 = *x = (*x + 1) % 256;
@@ -626,6 +752,92 @@ static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) {
   s->bufIdx = 0;
   if (last) {
     n = s->buf[15];
+    if (n < 1 || n > 16) { // this should never happen
+      n = 16;
+    }
+    for (i = 15; i >= n; --i) {
+      s->buf[i] = s->buf[i-n];
+    }
+    s->bufIdx = n;
+  }
+}
+
+//------------------------------------------------------------------------
+// AES-256 decryption
+//------------------------------------------------------------------------
+
+static void aes256KeyExpansion(DecryptAES256State *s,
+			       Guchar *objKey, int objKeyLen) {
+  Guint temp;
+  int i, round;
+
+  //~ this assumes objKeyLen == 32
+
+  for (i = 0; i < 8; ++i) {
+    s->w[i] = (objKey[4*i] << 24) + (objKey[4*i+1] << 16) +
+              (objKey[4*i+2] << 8) + objKey[4*i+3];
+  }
+  for (i = 8; i < 60; ++i) {
+    temp = s->w[i-1];
+    if ((i & 7) == 0) {
+      temp = subWord(rotWord(temp)) ^ rcon[i/8];
+    } else if ((i & 7) == 4) {
+      temp = subWord(temp);
+    }
+    s->w[i] = s->w[i-8] ^ temp;
+  }
+  for (round = 1; round <= 13; ++round) {
+    invMixColumnsW(&s->w[round * 4]);
+  }
+}
+
+static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last) {
+  int c, round, n, i;
+
+  // initial state
+  for (c = 0; c < 4; ++c) {
+    s->state[c] = in[4*c];
+    s->state[4+c] = in[4*c+1];
+    s->state[8+c] = in[4*c+2];
+    s->state[12+c] = in[4*c+3];
+  }
+
+  // round 0
+  addRoundKey(s->state, &s->w[14 * 4]);
+
+  // rounds 13-1
+  for (round = 13; round >= 1; --round) {
+    invSubBytes(s->state);
+    invShiftRows(s->state);
+    invMixColumns(s->state);
+    addRoundKey(s->state, &s->w[round * 4]);
+  }
+
+  // round 14
+  invSubBytes(s->state);
+  invShiftRows(s->state);
+  addRoundKey(s->state, &s->w[0]);
+
+  // CBC
+  for (c = 0; c < 4; ++c) {
+    s->buf[4*c] = s->state[c] ^ s->cbc[4*c];
+    s->buf[4*c+1] = s->state[4+c] ^ s->cbc[4*c+1];
+    s->buf[4*c+2] = s->state[8+c] ^ s->cbc[4*c+2];
+    s->buf[4*c+3] = s->state[12+c] ^ s->cbc[4*c+3];
+  }
+
+  // save the input block for the next CBC
+  for (i = 0; i < 16; ++i) {
+    s->cbc[i] = in[i];
+  }
+
+  // remove padding
+  s->bufIdx = 0;
+  if (last) {
+    n = s->buf[15];
+    if (n < 1 || n > 16) { // this should never happen
+      n = 16;
+    }
     for (i = 15; i >= n; --i) {
       s->buf[i] = s->buf[i-n];
     }
@@ -668,12 +880,17 @@ static inline Gulong md5Round4(Gulong a, Gulong b, Gulong c, Gulong d,
   return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s);
 }
 
-void Decrypt::md5(Guchar *msg, int msgLen, Guchar *digest) {
+void md5(Guchar *msg, int msgLen, Guchar *digest) {
   Gulong x[16];
   Gulong a, b, c, d, aa, bb, cc, dd;
   int n64;
   int i, j, k;
 
+  // sanity check
+  if (msgLen < 0) {
+    return;
+  }
+
   // compute number of 64-byte blocks
   // (length + pad byte (0x80) + 8 bytes for length)
   n64 = (msgLen + 1 + 8 + 63) / 64;
@@ -809,3 +1026,160 @@ void Decrypt::md5(Guchar *msg, int msgLen, Guchar *digest) {
   digest[14] = (Guchar)((d >>= 8) & 0xff);
   digest[15] = (Guchar)((d >>= 8) & 0xff);
 }
+
+//------------------------------------------------------------------------
+// SHA-256 hash
+//------------------------------------------------------------------------
+
+static Guint sha256K[64] = {
+  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+  0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+  0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+  0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+  0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+  0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+  0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+  0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+  0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+  0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+  0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+  0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+  0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+  0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+  0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+  0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+static inline Guint rotr(Guint x, Guint n) {
+  return (x >> n) | (x << (32 - n));
+}
+
+static inline Guint sha256Ch(Guint x, Guint y, Guint z) {
+  return (x & y) ^ (~x & z);
+}
+
+static inline Guint sha256Maj(Guint x, Guint y, Guint z) {
+  return (x & y) ^ (x & z) ^ (y & z);
+}
+
+static inline Guint sha256Sigma0(Guint x) {
+  return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
+}
+
+static inline Guint sha256Sigma1(Guint x) {
+  return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
+}
+
+static inline Guint sha256sigma0(Guint x) {
+  return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3);
+}
+
+static inline Guint sha256sigma1(Guint x) {
+  return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
+}
+
+void sha256HashBlock(Guchar *blk, Guint *H) {
+  Guint W[64];
+  Guint a, b, c, d, e, f, g, h;
+  Guint T1, T2;
+  Guint t;
+
+  // 1. prepare the message schedule
+  for (t = 0; t < 16; ++t) {
+    W[t] = (blk[t*4] << 24) |
+           (blk[t*4 + 1] << 16) |
+           (blk[t*4 + 2] << 8) |
+           blk[t*4 + 3];
+  }
+  for (t = 16; t < 64; ++t) {
+    W[t] = sha256sigma1(W[t-2]) + W[t-7] + sha256sigma0(W[t-15]) + W[t-16];
+  }
+
+  // 2. initialize the eight working variables
+  a = H[0];
+  b = H[1];
+  c = H[2];
+  d = H[3];
+  e = H[4];
+  f = H[5];
+  g = H[6];
+  h = H[7];
+
+  // 3.
+  for (t = 0; t < 64; ++t) {
+    T1 = h + sha256Sigma1(e) + sha256Ch(e,f,g) + sha256K[t] + W[t];
+    T2 = sha256Sigma0(a) + sha256Maj(a,b,c);
+    h = g;
+    g = f;
+    f = e;
+    e = d + T1;
+    d = c;
+    c = b;
+    b = a;
+    a = T1 + T2;
+  }
+
+  // 4. compute the intermediate hash value
+  H[0] += a;
+  H[1] += b;
+  H[2] += c;
+  H[3] += d;
+  H[4] += e;
+  H[5] += f;
+  H[6] += g;
+  H[7] += h;
+}
+
+static void sha256(Guchar *msg, int msgLen, Guchar *hash) {
+  Guchar blk[64];
+  Guint H[8];
+  int blkLen, i;
+
+  H[0] = 0x6a09e667;
+  H[1] = 0xbb67ae85;
+  H[2] = 0x3c6ef372;
+  H[3] = 0xa54ff53a;
+  H[4] = 0x510e527f;
+  H[5] = 0x9b05688c;
+  H[6] = 0x1f83d9ab;
+  H[7] = 0x5be0cd19;
+
+  blkLen = 0;
+  for (i = 0; i + 64 <= msgLen; i += 64) {
+    sha256HashBlock(msg + i, H);
+  }
+  blkLen = msgLen - i;
+  if (blkLen > 0) {
+    memcpy(blk, msg + i, blkLen);
+  }
+
+  // pad the message
+  blk[blkLen++] = 0x80;
+  if (blkLen > 56) {
+    while (blkLen < 64) {
+      blk[blkLen++] = 0;
+    }
+    sha256HashBlock(blk, H);
+    blkLen = 0;
+  }
+  while (blkLen < 56) {
+    blk[blkLen++] = 0;
+  }
+  blk[56] = 0;
+  blk[57] = 0;
+  blk[58] = 0;
+  blk[59] = 0;
+  blk[60] = (Guchar)(msgLen >> 21);
+  blk[61] = (Guchar)(msgLen >> 13);
+  blk[62] = (Guchar)(msgLen >> 5);
+  blk[63] = (Guchar)(msgLen << 3);
+  sha256HashBlock(blk, H);
+
+  // copy the output into the buffer (convert words to bytes)
+  for (i = 0; i < 8; ++i) {
+    hash[i*4]     = (Guchar)(H[i] >> 24);
+    hash[i*4 + 1] = (Guchar)(H[i] >> 16);
+    hash[i*4 + 2] = (Guchar)(H[i] >> 8);
+    hash[i*4 + 3] = (Guchar)H[i];
+  }
+}
diff --git a/poppler/Decrypt.h b/poppler/Decrypt.h
index feb7014..d947f41 100644
--- a/poppler/Decrypt.h
+++ b/poppler/Decrypt.h
@@ -39,7 +39,6 @@
 
 class Decrypt {
 public:
-  static void md5(Guchar *msg, int msgLen, Guchar *digest);
 
   // Generate a file key.  The <fileKey> buffer must have space for at
   // least 16 bytes.  Checks <ownerPassword> and then <userPassword>
@@ -48,6 +47,7 @@ public:
   // may be NULL, which is treated as an empty string.
   static GBool makeFileKey(int encVersion, int encRevision, int keyLength,
 			   GooString *ownerKey, GooString *userKey,
+			   GooString *ownerEnc, GooString *userEnc,
 			   int permissions, GooString *fileID,
 			   GooString *ownerPassword, GooString *userPassword,
 			   Guchar *fileKey, GBool encryptMetadata,
@@ -80,6 +80,14 @@ struct DecryptAESState {
   int bufIdx;
 };
 
+struct DecryptAES256State {
+  Guint w[60];
+  Guchar state[16];
+  Guchar cbc[16];
+  Guchar buf[16];
+  int bufIdx;
+};
+
 class DecryptStream: public FilterStream {
 public:
 
@@ -99,13 +107,20 @@ private:
 
   CryptAlgorithm algo;
   int objKeyLength;
-  Guchar objKey[16 + 9];
+  Guchar objKey[32];
   int charactersRead; // so that getPos() can be correct
 
   union {
     DecryptRC4State rc4;
     DecryptAESState aes;
+    DecryptAES256State aes256;
   } state;
 };
+ 
+//------------------------------------------------------------------------
+
+extern void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
+extern Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
+extern void md5(Guchar *msg, int msgLen, Guchar *digest);
 
 #endif
diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc
index 3092cea..51bb22b 100644
--- a/poppler/PDFDoc.cc
+++ b/poppler/PDFDoc.cc
@@ -1095,7 +1095,7 @@ void PDFDoc::writeTrailer(Guint uxrefOffset, int uxrefSize,
 
   //calculate md5 digest
   Guchar digest[16];
-  Decrypt::md5((Guchar*)message.getCString(), message.getLength(), digest);
+  md5((Guchar*)message.getCString(), message.getLength(), digest);
   obj1.initString(new GooString((const char*)digest, 16));
 
   //create ID array
diff --git a/poppler/SecurityHandler.cc b/poppler/SecurityHandler.cc
index 630c753..1e847c9 100644
--- a/poppler/SecurityHandler.cc
+++ b/poppler/SecurityHandler.cc
@@ -31,11 +31,6 @@
 #include "Decrypt.h"
 #include "Error.h"
 #include "GlobalParams.h"
-#if HAVE_XPDFCORE
-#  include "XPDFCore.h"
-#elif HAVE_WINPDFCORE
-#  include "WinPDFCore.h"
-#endif
 #ifdef ENABLE_PLUGINS
 #  include "XpdfPluginAPI.h"
 #endif
@@ -63,14 +58,15 @@ SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) {
       secHdlr = new ExternalSecurityHandler(docA, encryptDictA, xsh);
     } else {
 #endif
-      error(-1, "Couldn't find the '%s' security handler",
+      error(/*errSyntaxError, */-1, "Couldn't find the '%s' security handler",
 	    filterObj.getName());
       secHdlr = NULL;
 #ifdef ENABLE_PLUGINS
     }
 #endif
   } else {
-    error(-1, "Missing or invalid 'Filter' entry in encryption dictionary");
+    error(/*errSyntaxError, */-1,
+	  "Missing or invalid 'Filter' entry in encryption dictionary");
     secHdlr = NULL;
   }
   filterObj.free();
@@ -109,7 +105,7 @@ GBool SecurityHandler::checkEncryption(GooString *ownerPassword,
     }
   }
   if (!ok) {
-    error(-1, "Incorrect password");
+    error(/*errCommandLine, */-1, "Incorrect password");
   }
   return ok;
 }
@@ -144,8 +140,8 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
   SecurityHandler(docA)
 {
   Object versionObj, revisionObj, lengthObj;
-  Object ownerKeyObj, userKeyObj, permObj, fileIDObj;
-  Object fileIDObj1;
+  Object ownerKeyObj, userKeyObj, ownerEncObj, userEncObj;
+  Object permObj, fileIDObj, fileIDObj1;
   Object cryptFiltersObj, streamFilterObj, stringFilterObj;
   Object cryptFilterObj, cfmObj, cfLengthObj;
   Object encryptMetadataObj;
@@ -154,12 +150,17 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
   fileID = NULL;
   ownerKey = NULL;
   userKey = NULL;
+  ownerEnc = NULL;
+  userEnc = NULL;
+  fileKeyLength = 0;
 
   encryptDictA->dictLookup("V", &versionObj);
   encryptDictA->dictLookup("R", &revisionObj);
   encryptDictA->dictLookup("Length", &lengthObj);
   encryptDictA->dictLookup("O", &ownerKeyObj);
   encryptDictA->dictLookup("U", &userKeyObj);
+  encryptDictA->dictLookup("OE", &ownerEncObj);
+  encryptDictA->dictLookup("UE", &userEncObj);
   encryptDictA->dictLookup("P", &permObj);
   if (permObj.isUint()) {
       unsigned int permUint = permObj.getUint();
@@ -170,91 +171,137 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
   doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj);
   if (versionObj.isInt() &&
       revisionObj.isInt() &&
-      ownerKeyObj.isString() && ownerKeyObj.getString()->getLength() == 32 &&
-      userKeyObj.isString() && userKeyObj.getString()->getLength() == 32 &&
-      permObj.isInt()) {
+      permObj.isInt() &&
+      ownerKeyObj.isString() &&
+      userKeyObj.isString()) {
     encVersion = versionObj.getInt();
     encRevision = revisionObj.getInt();
-    encAlgorithm = cryptRC4;
-    // revision 2 forces a 40-bit key - some buggy PDF generators
-    // set the Length value incorrectly
-    if (encRevision == 2 || !lengthObj.isInt()) {
-      fileKeyLength = 5;
-    } else {
-      fileKeyLength = lengthObj.getInt() / 8;
-    }
-    encryptMetadata = gTrue;
-    //~ this currently only handles a subset of crypt filter functionality
-    if (encVersion == 4 && encRevision == 4) {
-      encryptDictA->dictLookup("CF", &cryptFiltersObj);
-      encryptDictA->dictLookup("StmF", &streamFilterObj);
-      encryptDictA->dictLookup("StrF", &stringFilterObj);
-      if (cryptFiltersObj.isDict() &&
-	  streamFilterObj.isName() &&
-	  stringFilterObj.isName() &&
-	  !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) {
-	if (cryptFiltersObj.dictLookup(streamFilterObj.getName(),
-				       &cryptFilterObj)->isDict()) {
-	  cryptFilterObj.dictLookup("CFM", &cfmObj);
-	  if (cfmObj.isName("V2")) {
-	    encVersion = 2;
-	    encRevision = 3;
-	    if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) {
-	      //~ according to the spec, this should be cfLengthObj / 8
-	      fileKeyLength = cfLengthObj.getInt();
-	    }
-	    cfLengthObj.free();
-	  } else if (cfmObj.isName("AESV2")) {
-	    encVersion = 2;
-	    encRevision = 3;
-	    encAlgorithm = cryptAES;
-	    if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) {
-	      //~ according to the spec, this should be cfLengthObj / 8
-	      fileKeyLength = cfLengthObj.getInt();
+    if ((encRevision <= 4 &&
+	 ownerKeyObj.getString()->getLength() == 32 &&
+	 userKeyObj.getString()->getLength() == 32) ||
+	(encRevision == 5 &&
+	 // the spec says 48 bytes, but Acrobat pads them out longer
+	 ownerKeyObj.getString()->getLength() >= 48 &&
+	 userKeyObj.getString()->getLength() >= 48 &&
+	 ownerEncObj.isString() &&
+	 ownerEncObj.getString()->getLength() == 32 &&
+	 userEncObj.isString() &&
+	 userEncObj.getString()->getLength() == 32)) {
+      encAlgorithm = cryptRC4;
+      // revision 2 forces a 40-bit key - some buggy PDF generators
+      // set the Length value incorrectly
+      if (encRevision == 2 || !lengthObj.isInt()) {
+	fileKeyLength = 5;
+      } else {
+	fileKeyLength = lengthObj.getInt() / 8;
+      }
+      encryptMetadata = gTrue;
+      //~ this currently only handles a subset of crypt filter functionality
+      //~ (in particular, it ignores the EFF entry in encryptDictA, and
+      //~ doesn't handle the case where StmF, StrF, and EFF are not all the
+      //~ same)
+      if ((encVersion == 4 || encVersion == 5) &&
+	  (encRevision == 4 || encRevision == 5)) {
+	encryptDictA->dictLookup("CF", &cryptFiltersObj);
+	encryptDictA->dictLookup("StmF", &streamFilterObj);
+	encryptDictA->dictLookup("StrF", &stringFilterObj);
+	if (cryptFiltersObj.isDict() &&
+	    streamFilterObj.isName() &&
+	    stringFilterObj.isName() &&
+	    !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) {
+	  if (!strcmp(streamFilterObj.getName(), "Identity")) {
+	    // no encryption on streams or strings
+	    encVersion = encRevision = -1;
+	  } else {
+	    if (cryptFiltersObj.dictLookup(streamFilterObj.getName(),
+					   &cryptFilterObj)->isDict()) {
+	      cryptFilterObj.dictLookup("CFM", &cfmObj);
+	      if (cfmObj.isName("V2")) {
+		encVersion = 2;
+		encRevision = 3;
+		if (cryptFilterObj.dictLookup("Length",
+					      &cfLengthObj)->isInt()) {
+		  //~ according to the spec, this should be cfLengthObj / 8
+		  fileKeyLength = cfLengthObj.getInt();
+		}
+		cfLengthObj.free();
+	      } else if (cfmObj.isName("AESV2")) {
+		encVersion = 2;
+		encRevision = 3;
+		encAlgorithm = cryptAES;
+		if (cryptFilterObj.dictLookup("Length",
+					      &cfLengthObj)->isInt()) {
+		  //~ according to the spec, this should be cfLengthObj / 8
+		  fileKeyLength = cfLengthObj.getInt();
+		}
+		cfLengthObj.free();
+	      } else if (cfmObj.isName("AESV3")) {
+		encVersion = 5;
+		encRevision = 5;
+		encAlgorithm = cryptAES256;
+		if (cryptFilterObj.dictLookup("Length",
+					      &cfLengthObj)->isInt()) {
+		  //~ according to the spec, this should be cfLengthObj / 8
+		  fileKeyLength = cfLengthObj.getInt();
+		}
+		cfLengthObj.free();
+	      }
+	      cfmObj.free();
 	    }
-	    cfLengthObj.free();
+	    cryptFilterObj.free();
 	  }
-	  cfmObj.free();
 	}
-	cryptFilterObj.free();
-      }
-      stringFilterObj.free();
-      streamFilterObj.free();
-      cryptFiltersObj.free();
-      if (encryptDictA->dictLookup("EncryptMetadata",
-				   &encryptMetadataObj)->isBool()) {
-	encryptMetadata = encryptMetadataObj.getBool();
+	stringFilterObj.free();
+	streamFilterObj.free();
+	cryptFiltersObj.free();
+	if (encryptDictA->dictLookup("EncryptMetadata",
+				     &encryptMetadataObj)->isBool()) {
+	  encryptMetadata = encryptMetadataObj.getBool();
+	}
+	encryptMetadataObj.free();
       }
-      encryptMetadataObj.free();
-    }
-    permFlags = permObj.getInt();
-    ownerKey = ownerKeyObj.getString()->copy();
-    userKey = userKeyObj.getString()->copy();
-    if (encVersion >= 1 && encVersion <= 2 &&
-	encRevision >= 2 && encRevision <= 3) {
-      if (fileIDObj.isArray()) {
-	if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) {
-	  fileID = fileIDObj1.getString()->copy();
+      permFlags = permObj.getInt();
+      ownerKey = ownerKeyObj.getString()->copy();
+      userKey = userKeyObj.getString()->copy();
+      if (encVersion >= 1 && encVersion <= 2 &&
+	  encRevision >= 2 && encRevision <= 3) {
+	if (fileIDObj.isArray()) {
+	  if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) {
+	    fileID = fileIDObj1.getString()->copy();
+	  } else {
+	    fileID = new GooString();
+	  }
+	  fileIDObj1.free();
 	} else {
 	  fileID = new GooString();
 	}
-	fileIDObj1.free();
-      } else {
-	fileID = new GooString();
+	if (fileKeyLength > 16 || fileKeyLength < 0) {
+	  fileKeyLength = 16;
+	}
+	ok = gTrue;
+      } else if (encVersion == 5 && encRevision == 5) {
+	fileID = new GooString(); // unused for V=R=5
+	ownerEnc = ownerEncObj.getString()->copy();
+	userEnc = userEncObj.getString()->copy();
+	if (fileKeyLength > 32 || fileKeyLength < 0) {
+	  fileKeyLength = 32;
+	}
+	ok = gTrue;
+      } else if (!(encVersion == -1 && encRevision == -1)) {
+	error(/*errUnimplemented, */-1,
+	      "Unsupported version/revision (%d/%d) of Standard security handler",
+	      encVersion, encRevision);
       }
-      ok = gTrue;
     } else {
-      error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
-	    encVersion, encRevision);
+      error(/*errSyntaxError, */-1, "Invalid encryption key length");
     }
   } else {
-    error(-1, "Weird encryption info");
-  }
-  if (fileKeyLength > 16) {
-    fileKeyLength = 16;
+    error(/*errSyntaxError, */-1, "Weird encryption info");
   }
   fileIDObj.free();
   permObj.free();
+  userEncObj.free();
+  ownerEncObj.free();
   userKeyObj.free();
   ownerKeyObj.free();
   lengthObj.free();
@@ -272,6 +319,16 @@ StandardSecurityHandler::~StandardSecurityHandler() {
   if (userKey) {
     delete userKey;
   }
+  if (ownerEnc) {
+    delete ownerEnc;
+  }
+  if (userEnc) {
+    delete userEnc;
+  }
+}
+
+GBool StandardSecurityHandler::isUnencrypted() {
+  return encVersion == -1 && encRevision == -1;
 }
 
 void *StandardSecurityHandler::makeAuthData(GooString *ownerPassword,
@@ -283,27 +340,7 @@ void *StandardSecurityHandler::makeAuthData(GooString *ownerPassword,
 }
 
 void *StandardSecurityHandler::getAuthData() {
-#if HAVE_XPDFCORE
-  XPDFCore *core;
-  GooString *password;
-
-  if (!(core = (XPDFCore *)doc->getGUIData()) ||
-      !(password = core->getPassword())) {
-    return NULL;
-  }
-  return new StandardAuthData(password, password->copy());
-#elif HAVE_WINPDFCORE
-  WinPDFCore *core;
-  GooString *password;
-
-  if (!(core = (WinPDFCore *)doc->getGUIData()) ||
-      !(password = core->getPassword())) {
-    return NULL;
-  }
-  return new StandardAuthData(password, password->copy());
-#else
   return NULL;
-#endif
 }
 
 void StandardSecurityHandler::freeAuthData(void *authData) {
@@ -324,7 +361,8 @@ GBool StandardSecurityHandler::authorize(void *authData) {
     userPassword = NULL;
   }
   if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength,
-			    ownerKey, userKey, permFlags, fileID,
+			    ownerKey, userKey, ownerEnc, userEnc,
+			    permFlags, fileID,
 			    ownerPassword, userPassword, fileKey,
 			    encryptMetadata, &ownerPasswordOk)) {
     return gFalse;
diff --git a/poppler/SecurityHandler.h b/poppler/SecurityHandler.h
index 5278a6c..57cb2d2 100644
--- a/poppler/SecurityHandler.h
+++ b/poppler/SecurityHandler.h
@@ -34,6 +34,9 @@ public:
   SecurityHandler(PDFDoc *docA);
   virtual ~SecurityHandler();
 
+  // Returns true if the file is actually unencrypted.
+  virtual GBool isUnencrypted() { return gFalse; }
+
   // Check the document's encryption.  If the document is encrypted,
   // this will first try <ownerPassword> and <userPassword> (in
   // "batch" mode), and if those fail, it will attempt to request a
@@ -92,6 +95,7 @@ public:
   StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA);
   virtual ~StandardSecurityHandler();
 
+  virtual GBool isUnencrypted();
   virtual void *makeAuthData(GooString *ownerPassword,
 			     GooString *userPassword);
   virtual void *getAuthData();
@@ -109,7 +113,7 @@ private:
 
   int permFlags;
   GBool ownerPasswordOk;
-  Guchar fileKey[16];
+  Guchar fileKey[32];
   int fileKeyLength;
   int encVersion;
   int encRevision;
@@ -117,6 +121,7 @@ private:
   CryptAlgorithm encAlgorithm;
 
   GooString *ownerKey, *userKey;
+  GooString *ownerEnc, *userEnc;
   GooString *fileID;
   GBool ok;
 };
diff --git a/poppler/Stream.h b/poppler/Stream.h
index 6ede153..138402d 100644
--- a/poppler/Stream.h
+++ b/poppler/Stream.h
@@ -71,7 +71,8 @@ enum StreamColorSpaceMode {
 // include file dependency loops.
 enum CryptAlgorithm {
   cryptRC4,
-  cryptAES
+  cryptAES,
+  cryptAES256
 };
 
 //------------------------------------------------------------------------


More information about the poppler mailing list