f8a0a1
diff --git a/config.h.cmake b/config.h.cmake
f8a0a1
index 7989cbfb..6f5e147e 100644
f8a0a1
--- a/config.h.cmake
f8a0a1
+++ b/config.h.cmake
f8a0a1
@@ -24,9 +24,6 @@
f8a0a1
 /* Use zlib instead of builtin zlib decoder to uncompress flate streams. */
f8a0a1
 #cmakedefine ENABLE_ZLIB_UNCOMPRESS 1
f8a0a1
 
f8a0a1
-/* Build against libnss3 for digital signature validation */
f8a0a1
-#cmakedefine ENABLE_NSS3 1
f8a0a1
-
f8a0a1
 /* Use cairo for rendering. */
f8a0a1
 #cmakedefine HAVE_CAIRO 1
f8a0a1
 
f8a0a1
diff --git a/poppler/Decrypt.cc b/poppler/Decrypt.cc
f8a0a1
index 16476f4f..9f4adda3 100644
f8a0a1
--- a/poppler/Decrypt.cc
f8a0a1
+++ b/poppler/Decrypt.cc
f8a0a1
@@ -39,17 +39,33 @@
f8a0a1
 #include "goo/grandom.h"
f8a0a1
 #include "Decrypt.h"
f8a0a1
 #include "Error.h"
f8a0a1
-
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+#include <nss.h>
f8a0a1
+#include <prerror.h>
f8a0a1
+#include <sechash.h>
f8a0a1
+#endif
f8a0a1
+
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+static PK11Context *rc4InitContext(const Guchar *key, int keyLen);
f8a0a1
+static Guchar rc4DecryptByte(PK11Context *context, Guchar c);
f8a0a1
+#else
f8a0a1
 static void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
f8a0a1
 static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
f8a0a1
+#endif
f8a0a1
 
f8a0a1
 static GBool aesReadBlock(Stream  *str, Guchar *in, GBool addPadding);
f8a0a1
 
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+static PK11Context *aesInitContext(Guchar *in, Guchar *objKey, int objKeyLength, GBool decrypt);
f8a0a1
+#else
f8a0a1
 static void aesKeyExpansion(DecryptAESState *s, Guchar *objKey, int objKeyLen, GBool decrypt);
f8a0a1
+#endif
f8a0a1
 static void aesEncryptBlock(DecryptAESState *s, Guchar *in);
f8a0a1
 static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);
f8a0a1
 
f8a0a1
+#ifndef ENABLE_NSS3
f8a0a1
 static void aes256KeyExpansion(DecryptAES256State *s, Guchar *objKey, int objKeyLen, GBool decrypt);
f8a0a1
+#endif
f8a0a1
 static void aes256EncryptBlock(DecryptAES256State *s, Guchar *in);
f8a0a1
 static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last);
f8a0a1
 
f8a0a1
@@ -70,6 +86,31 @@ static const Guchar passwordPad[32] = {
f8a0a1
 // Decrypt
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
 
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+static void shutdownNSS()
f8a0a1
+{
f8a0a1
+  if (NSS_Shutdown() != SECSuccess) {
f8a0a1
+    error(errInternal, -1, "NSS shutdown failed: {0:s}",
f8a0a1
+          PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
f8a0a1
+  }
f8a0a1
+}
f8a0a1
+
f8a0a1
+static GBool initNSS() {
f8a0a1
+  if (NSS_IsInitialized()) {
f8a0a1
+    return gTrue;
f8a0a1
+  } else {
f8a0a1
+    if (NSS_NoDB_Init(".") != SECSuccess) {
f8a0a1
+      error(errInternal, -1, "NSS initialization failed: {0:s}",
f8a0a1
+            PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
f8a0a1
+      return gFalse;
f8a0a1
+    } else {
f8a0a1
+      atexit(shutdownNSS);
f8a0a1
+      return gTrue;
f8a0a1
+    }
f8a0a1
+  }
f8a0a1
+}
f8a0a1
+#endif
f8a0a1
+
f8a0a1
 GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
f8a0a1
 			   GooString *ownerKey, GooString *userKey,
f8a0a1
 			   GooString *ownerEnc, GooString *userEnc,
f8a0a1
@@ -80,13 +121,21 @@ GBool Decrypt::makeFileKey(int encVersio
f8a0a1
   DecryptAES256State state;
f8a0a1
   Guchar test[127 + 56], test2[32];
f8a0a1
   GooString *userPassword2;
f8a0a1
-  Guchar fState[256];
f8a0a1
   Guchar tmpKey[16];
f8a0a1
-  Guchar fx, fy;
f8a0a1
   int len, i, j;
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  PK11Context *rc4Context;
f8a0a1
+#else
f8a0a1
+  Guchar fState[256];
f8a0a1
+  Guchar fx, fy;
f8a0a1
+#endif
f8a0a1
 
f8a0a1
   *ownerPasswordOk = gFalse;
f8a0a1
 
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  initNSS();
f8a0a1
+#endif
f8a0a1
+
f8a0a1
   if (encRevision == 5 || encRevision == 6) {
f8a0a1
 
f8a0a1
     // check the owner password
f8a0a1
@@ -115,15 +164,27 @@ GBool Decrypt::makeFileKey(int encVersio
f8a0a1
 	  //test contains the initial SHA-256 hash input K.
f8a0a1
 	  revision6Hash(ownerPassword, test, userKey->getCString());
f8a0a1
 	}
f8a0a1
+#ifndef ENABLE_NSS3
f8a0a1
 	aes256KeyExpansion(&state, test, 32, gTrue);
f8a0a1
+#endif
f8a0a1
 	for (i = 0; i < 16; ++i) {
f8a0a1
 	  state.cbc[i] = 0;
f8a0a1
 	}
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+	state.context = aesInitContext(state.cbc, test, 32, gTrue);
f8a0a1
+	if (state.context) {
f8a0a1
+#endif
f8a0a1
 	aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString(), gFalse);
f8a0a1
 	memcpy(fileKey, state.buf, 16);
f8a0a1
 	aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString() + 16,
f8a0a1
 			   gFalse);
f8a0a1
 	memcpy(fileKey + 16, state.buf, 16);
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+	PK11_DestroyContext(state.context, PR_TRUE);
f8a0a1
+	} else {
f8a0a1
+	  return gFalse;
f8a0a1
+	}
f8a0a1
+#endif
f8a0a1
 
f8a0a1
 	*ownerPasswordOk = gTrue;
f8a0a1
 	return gTrue;
f8a0a1
@@ -156,15 +217,27 @@ GBool Decrypt::makeFileKey(int encVersio
f8a0a1
 	  //user key is not used in computing intermediate user key.
f8a0a1
 	  revision6Hash(userPassword, test, nullptr);
f8a0a1
 	}
f8a0a1
+#ifndef ENABLE_NSS3
f8a0a1
 	aes256KeyExpansion(&state, test, 32, gTrue);
f8a0a1
+#endif
f8a0a1
 	for (i = 0; i < 16; ++i) {
f8a0a1
 	  state.cbc[i] = 0;
f8a0a1
 	}
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+	state.context = aesInitContext(state.cbc, test, 32, gTrue);
f8a0a1
+	if (state.context) {
f8a0a1
+#endif
f8a0a1
 	aes256DecryptBlock(&state, (Guchar *)userEnc->getCString(), gFalse);
f8a0a1
 	memcpy(fileKey, state.buf, 16);
f8a0a1
 	aes256DecryptBlock(&state, (Guchar *)userEnc->getCString() + 16,
f8a0a1
 			   gFalse);
f8a0a1
 	memcpy(fileKey + 16, state.buf, 16);
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+	PK11_DestroyContext(state.context, PR_TRUE);
f8a0a1
+	} else {
f8a0a1
+	  return gFalse;
f8a0a1
+	}
f8a0a1
+#endif
f8a0a1
 
f8a0a1
 	return gTrue;
f8a0a1
       }
f8a0a1
@@ -189,22 +262,41 @@ GBool Decrypt::makeFileKey(int encVersio
f8a0a1
 	}
f8a0a1
       }
f8a0a1
       if (encRevision == 2) {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+	rc4Context = rc4InitContext(test, keyLength);
f8a0a1
+	if (rc4Context) {
f8a0a1
+	  for (i = 0; i < 32; ++i)
f8a0a1
+	    test2[i] = rc4DecryptByte(rc4Context, ownerKey->getChar(i));
f8a0a1
+	  PK11_DestroyContext(rc4Context, PR_TRUE);
f8a0a1
+	}
f8a0a1
+#else
f8a0a1
 	rc4InitKey(test, keyLength, fState);
f8a0a1
 	fx = fy = 0;
f8a0a1
 	for (i = 0; i < 32; ++i) {
f8a0a1
 	  test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
f8a0a1
 	}
f8a0a1
+#endif
f8a0a1
       } else {
f8a0a1
 	memcpy(test2, ownerKey->getCString(), 32);
f8a0a1
 	for (i = 19; i >= 0; --i) {
f8a0a1
 	  for (j = 0; j < keyLength; ++j) {
f8a0a1
 	    tmpKey[j] = test[j] ^ i;
f8a0a1
 	  }
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+	  rc4Context = rc4InitContext(tmpKey, keyLength);
f8a0a1
+	  if (rc4Context) {
f8a0a1
+	    for (j = 0; j < 32; ++j) {
f8a0a1
+	      test2[j] = rc4DecryptByte(rc4Context, test2[j]);
f8a0a1
+	    }
f8a0a1
+	    PK11_DestroyContext(rc4Context, PR_TRUE);
f8a0a1
+	  }
f8a0a1
+#else
f8a0a1
 	  rc4InitKey(tmpKey, keyLength, fState);
f8a0a1
 	  fx = fy = 0;
f8a0a1
 	  for (j = 0; j < 32; ++j) {
f8a0a1
 	    test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
f8a0a1
 	  }
f8a0a1
+#endif
f8a0a1
 	}
f8a0a1
       }
f8a0a1
       userPassword2 = new GooString((char *)test2, 32);
f8a0a1
@@ -232,11 +324,15 @@ GBool Decrypt::makeFileKey2(int encVersi
f8a0a1
 			    GBool encryptMetadata) {
f8a0a1
   Guchar *buf;
f8a0a1
   Guchar test[32];
f8a0a1
-  Guchar fState[256];
f8a0a1
   Guchar tmpKey[16];
f8a0a1
-  Guchar fx, fy;
f8a0a1
   int len, i, j;
f8a0a1
-  GBool ok;
f8a0a1
+  GBool ok = gTrue;
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  PK11Context *rc4Context;
f8a0a1
+#else
f8a0a1
+  Guchar fState[256];
f8a0a1
+  Guchar fx, fy;
f8a0a1
+#endif
f8a0a1
 
f8a0a1
   // generate file key
f8a0a1
   buf = (Guchar *)gmalloc(72 + fileID->getLength());
f8a0a1
@@ -273,28 +369,52 @@ GBool Decrypt::makeFileKey2(int encVersi
f8a0a1
 
f8a0a1
   // test user password
f8a0a1
   if (encRevision == 2) {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    rc4Context = rc4InitContext(fileKey, keyLength);
f8a0a1
+    if (rc4Context) {
f8a0a1
+      for (i = 0; i < 32; ++i)
f8a0a1
+        test[i] = rc4DecryptByte(rc4Context, userKey->getChar(i));
f8a0a1
+      PK11_DestroyContext(rc4Context, PR_TRUE);
f8a0a1
+    } else {
f8a0a1
+      ok = gFalse;
f8a0a1
+    }
f8a0a1
+#else
f8a0a1
     rc4InitKey(fileKey, keyLength, fState);
f8a0a1
     fx = fy = 0;
f8a0a1
     for (i = 0; i < 32; ++i) {
f8a0a1
       test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i));
f8a0a1
     }
f8a0a1
-    ok = memcmp(test, passwordPad, 32) == 0;
f8a0a1
+#endif
f8a0a1
+    if (ok)
f8a0a1
+      ok = memcmp(test, passwordPad, 32) == 0;
f8a0a1
   } else if (encRevision == 3) {
f8a0a1
     memcpy(test, userKey->getCString(), 32);
f8a0a1
     for (i = 19; i >= 0; --i) {
f8a0a1
       for (j = 0; j < keyLength; ++j) {
f8a0a1
 	tmpKey[j] = fileKey[j] ^ i;
f8a0a1
       }
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+      rc4Context = rc4InitContext(tmpKey, keyLength);
f8a0a1
+      if (rc4Context) {
f8a0a1
+        for (j = 0; j < 32; ++j)
f8a0a1
+          test[j] = rc4DecryptByte(rc4Context, test[j]);
f8a0a1
+        PK11_DestroyContext(rc4Context, PR_TRUE);
f8a0a1
+      } else {
f8a0a1
+        ok = gFalse;
f8a0a1
+      }
f8a0a1
+#else
f8a0a1
       rc4InitKey(tmpKey, keyLength, fState);
f8a0a1
       fx = fy = 0;
f8a0a1
       for (j = 0; j < 32; ++j) {
f8a0a1
 	test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]);
f8a0a1
       }
f8a0a1
+#endif
f8a0a1
     }
f8a0a1
     memcpy(buf, passwordPad, 32);
f8a0a1
     memcpy(buf + 32, fileID->getCString(), fileID->getLength());
f8a0a1
     md5(buf, 32 + fileID->getLength(), buf);
f8a0a1
-    ok = memcmp(test, buf, 16) == 0;
f8a0a1
+    if (ok)
f8a0a1
+      ok = memcmp(test, buf, 16) == 0;
f8a0a1
   } else {
f8a0a1
     ok = gFalse;
f8a0a1
   }
f8a0a1
@@ -334,6 +454,9 @@ BaseCryptStream::BaseCryptStream(Stream
f8a0a1
     if ((objKeyLength = keyLength + 5) > 16) {
f8a0a1
       objKeyLength = 16;
f8a0a1
     }
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    state.rc4.context = nullptr;
f8a0a1
+#endif
f8a0a1
     break;
f8a0a1
   case cryptAES:
f8a0a1
     objKey[keyLength] = objNum & 0xff;
f8a0a1
@@ -349,9 +472,15 @@ BaseCryptStream::BaseCryptStream(Stream
f8a0a1
     if ((objKeyLength = keyLength + 5) > 16) {
f8a0a1
       objKeyLength = 16;
f8a0a1
     }
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    state.aes.context = nullptr;
f8a0a1
+#endif
f8a0a1
     break;
f8a0a1
   case cryptAES256:
f8a0a1
     objKeyLength = keyLength;
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    state.aes256.context = nullptr;
f8a0a1
+#endif
f8a0a1
     break;
f8a0a1
   case cryptNone:
f8a0a1
     break;
f8a0a1
@@ -359,9 +488,32 @@ BaseCryptStream::BaseCryptStream(Stream
f8a0a1
 
f8a0a1
   charactersRead = 0;
f8a0a1
   autoDelete = gTrue;
f8a0a1
+
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  initNSS();
f8a0a1
+#endif
f8a0a1
 }
f8a0a1
 
f8a0a1
 BaseCryptStream::~BaseCryptStream() {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  switch (algo) {
f8a0a1
+  case cryptRC4:
f8a0a1
+    if (state.rc4.context)
f8a0a1
+      PK11_DestroyContext(state.rc4.context, PR_TRUE);
f8a0a1
+    break;
f8a0a1
+  case cryptAES:
f8a0a1
+    if (state.aes.context)
f8a0a1
+      PK11_DestroyContext(state.aes.context, PR_TRUE);
f8a0a1
+    break;
f8a0a1
+  case cryptAES256:
f8a0a1
+    if (state.aes256.context)
f8a0a1
+      PK11_DestroyContext(state.aes256.context, PR_TRUE);
f8a0a1
+    break;
f8a0a1
+  default:
f8a0a1
+    break;
f8a0a1
+  }
f8a0a1
+#endif
f8a0a1
+
f8a0a1
   if (autoDelete) {
f8a0a1
     delete str;
f8a0a1
   }
f8a0a1
@@ -424,18 +576,40 @@ void EncryptStream::reset() {
f8a0a1
 
f8a0a1
   switch (algo) {
f8a0a1
   case cryptRC4:
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    if (state.rc4.context)
f8a0a1
+      PK11_DestroyContext(state.rc4.context, PR_TRUE);
f8a0a1
+    state.rc4.context = rc4InitContext(objKey, objKeyLength);
f8a0a1
+#else
f8a0a1
     state.rc4.x = state.rc4.y = 0;
f8a0a1
     rc4InitKey(objKey, objKeyLength, state.rc4.state);
f8a0a1
+#endif
f8a0a1
     break;
f8a0a1
   case cryptAES:
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf
f8a0a1
+    if (state.aes.context)
f8a0a1
+      PK11_DestroyContext(state.aes.context, PR_TRUE);
f8a0a1
+    state.aes.context = aesInitContext(state.aes.cbc, objKey, objKeyLength,
f8a0a1
+                                       gFalse);
f8a0a1
+#else
f8a0a1
     aesKeyExpansion(&state.aes, objKey, objKeyLength, gFalse);
f8a0a1
     memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf
f8a0a1
+#endif
f8a0a1
     state.aes.bufIdx = 0;
f8a0a1
     state.aes.paddingReached = gFalse;
f8a0a1
     break;
f8a0a1
   case cryptAES256:
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf
f8a0a1
+    if (state.aes256.context)
f8a0a1
+      PK11_DestroyContext(state.aes256.context, PR_TRUE);
f8a0a1
+    state.aes256.context = aesInitContext(state.aes256.cbc, objKey, objKeyLength,
f8a0a1
+                                          gFalse);
f8a0a1
+#else
f8a0a1
     aes256KeyExpansion(&state.aes256, objKey, objKeyLength, gFalse);
f8a0a1
     memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf
f8a0a1
+#endif
f8a0a1
     state.aes256.bufIdx = 0;
f8a0a1
     state.aes256.paddingReached = gFalse;
f8a0a1
     break;
f8a0a1
@@ -456,7 +630,11 @@ int EncryptStream::lookChar() {
f8a0a1
   case cryptRC4:
f8a0a1
     if ((c = str->getChar()) != EOF) {
f8a0a1
       // RC4 is XOR-based: the decryption algorithm works for encryption too
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+      c = rc4DecryptByte(state.rc4.context, (Guchar)c);
f8a0a1
+#else
f8a0a1
       c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (Guchar)c);
f8a0a1
+#endif
f8a0a1
     }
f8a0a1
     break;
f8a0a1
   case cryptAES:
f8a0a1
@@ -506,21 +684,47 @@ void DecryptStream::reset() {
f8a0a1
 
f8a0a1
   switch (algo) {
f8a0a1
   case cryptRC4:
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    if (state.rc4.context)
f8a0a1
+      PK11_DestroyContext(state.rc4.context, PR_TRUE);
f8a0a1
+    state.rc4.context = rc4InitContext(objKey, objKeyLength);
f8a0a1
+#else
f8a0a1
     state.rc4.x = state.rc4.y = 0;
f8a0a1
     rc4InitKey(objKey, objKeyLength, state.rc4.state);
f8a0a1
+#endif
f8a0a1
     break;
f8a0a1
   case cryptAES:
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    if (state.aes.context)
f8a0a1
+      PK11_DestroyContext(state.aes.context, PR_TRUE);
f8a0a1
+    for (i = 0; i < 16; ++i) {
f8a0a1
+      state.aes.cbc[i] = str->getChar();
f8a0a1
+    }
f8a0a1
+    state.aes.context = aesInitContext(state.aes.cbc, objKey, objKeyLength,
f8a0a1
+                                       gTrue);
f8a0a1
+#else
f8a0a1
     aesKeyExpansion(&state.aes, objKey, objKeyLength, gTrue);
f8a0a1
     for (i = 0; i < 16; ++i) {
f8a0a1
       state.aes.cbc[i] = str->getChar();
f8a0a1
     }
f8a0a1
+#endif
f8a0a1
     state.aes.bufIdx = 16;
f8a0a1
     break;
f8a0a1
   case cryptAES256:
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    if (state.aes256.context)
f8a0a1
+      PK11_DestroyContext(state.aes256.context, PR_TRUE);
f8a0a1
+    for (i = 0; i < 16; ++i) {
f8a0a1
+      state.aes256.cbc[i] = str->getChar();
f8a0a1
+    }
f8a0a1
+    state.aes256.context = aesInitContext(state.aes256.cbc, objKey, objKeyLength,
f8a0a1
+                                          gTrue);
f8a0a1
+#else
f8a0a1
     aes256KeyExpansion(&state.aes256, objKey, objKeyLength, gTrue);
f8a0a1
     for (i = 0; i < 16; ++i) {
f8a0a1
       state.aes256.cbc[i] = str->getChar();
f8a0a1
     }
f8a0a1
+#endif
f8a0a1
     state.aes256.bufIdx = 16;
f8a0a1
     break;
f8a0a1
   case cryptNone:
f8a0a1
@@ -539,10 +743,21 @@ int DecryptStream::lookChar() {
f8a0a1
   switch (algo) {
f8a0a1
   case cryptRC4:
f8a0a1
     if ((c = str->getChar()) != EOF) {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+      if (unlikely(state.rc4.context == nullptr))
f8a0a1
+        c = EOF;
f8a0a1
+      else
f8a0a1
+        c = rc4DecryptByte(state.rc4.context, (Guchar)c);
f8a0a1
+#else
f8a0a1
       c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (Guchar)c);
f8a0a1
+#endif
f8a0a1
     }
f8a0a1
     break;
f8a0a1
   case cryptAES:
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    if (unlikely(state.aes.context == nullptr))
f8a0a1
+      break;
f8a0a1
+#endif
f8a0a1
     if (state.aes.bufIdx == 16) {
f8a0a1
       if (aesReadBlock(str, in, gFalse)) {
f8a0a1
         aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
f8a0a1
@@ -555,6 +770,10 @@ int DecryptStream::lookChar() {
f8a0a1
     }
f8a0a1
     break;
f8a0a1
   case cryptAES256:
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    if (unlikely(state.aes256.context == nullptr))
f8a0a1
+      break;
f8a0a1
+#endif
f8a0a1
     if (state.aes256.bufIdx == 16) {
f8a0a1
       if (aesReadBlock(str, in, gFalse)) {
f8a0a1
         aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
f8a0a1
@@ -576,6 +795,175 @@ int DecryptStream::lookChar() {
f8a0a1
 // RC4-compatible decryption
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
 
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+/*
f8a0a1
+ * This function turns given key into token key (compared to a session key
f8a0a1
+ * which is prohibited in FIPS mode).
f8a0a1
+ */
f8a0a1
+static PK11SymKey *tokenizeKey(const Guchar *key, int keyLen,
f8a0a1
+                               CK_ATTRIBUTE_TYPE operation) {
f8a0a1
+  CK_MECHANISM_TYPE  cipherMech = CKM_AES_CBC_PAD;
f8a0a1
+  PK11SlotInfo      *slot;
f8a0a1
+  PK11SymKey        *wrappingKey = nullptr;
f8a0a1
+  PK11SymKey        *symKey = nullptr;
f8a0a1
+  PK11SymKey        *token = nullptr;
f8a0a1
+  SECStatus          retval;
f8a0a1
+  SECItem           *secParam = nullptr;
f8a0a1
+  SECItem            ivItem, wrappedKey;
f8a0a1
+  Guchar      output[48];   // Buffer to hold 256 bit key + padding
f8a0a1
+  Guchar      iv[16];       // Initialization vector for AES
f8a0a1
+  Guint       outputLength;
f8a0a1
+  int                i;
f8a0a1
+
f8a0a1
+  slot = PK11_GetBestSlot(CKM_AES_KEY_GEN, nullptr);
f8a0a1
+  if (slot == nullptr) {
f8a0a1
+    error(errInternal, -1, "Unable to find security device (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  // Generate random key for wrapping of given key by AES-256
f8a0a1
+  wrappingKey = PK11_KeyGen(slot, CKM_AES_KEY_GEN, nullptr, 32, nullptr);
f8a0a1
+  if (wrappingKey == nullptr) {
f8a0a1
+    error(errInternal, -1, "Failed to generate wrapping key (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  for (i = 0; i < 16; i++)
f8a0a1
+    iv[i] = i;
f8a0a1
+
f8a0a1
+  ivItem.type = siBuffer;
f8a0a1
+  ivItem.data = iv;
f8a0a1
+  ivItem.len = 16;
f8a0a1
+
f8a0a1
+  secParam = PK11_ParamFromIV(cipherMech, &ivItem);
f8a0a1
+  if (secParam == nullptr) {
f8a0a1
+    error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  // Encrypt given key
f8a0a1
+  retval = PK11_Encrypt(wrappingKey,
f8a0a1
+                        cipherMech,
f8a0a1
+                        secParam,
f8a0a1
+                        output,
f8a0a1
+                        &outputLength,
f8a0a1
+                        sizeof(output),
f8a0a1
+                        key,
f8a0a1
+                        keyLen);
f8a0a1
+  if (retval != SECSuccess) {
f8a0a1
+    error(errInternal, -1, "Failed to encrypt key (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  wrappedKey.type = siBuffer;
f8a0a1
+  wrappedKey.data = output;
f8a0a1
+  wrappedKey.len = outputLength;
f8a0a1
+
f8a0a1
+  // Unwrap the wrapped key to token so it can be used in FIPS mode
f8a0a1
+  token = PK11_UnwrapSymKey(wrappingKey,
f8a0a1
+                            cipherMech,
f8a0a1
+                            &ivItem,
f8a0a1
+                            &wrappedKey,
f8a0a1
+                            operation,
f8a0a1
+                            CKA_UNWRAP,
f8a0a1
+                            keyLen);
f8a0a1
+
f8a0a1
+  if (token == nullptr) {
f8a0a1
+    error(errInternal, -1, "Failed to unwrap symmetric key (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+  }
f8a0a1
+
f8a0a1
+err:
f8a0a1
+  if (secParam != nullptr)
f8a0a1
+    SECITEM_FreeItem(secParam, PR_TRUE);
f8a0a1
+
f8a0a1
+  if (wrappingKey != nullptr)
f8a0a1
+    PK11_FreeSymKey(wrappingKey);
f8a0a1
+
f8a0a1
+  if (symKey != nullptr)
f8a0a1
+    PK11_FreeSymKey(symKey);
f8a0a1
+
f8a0a1
+  if (slot != nullptr)
f8a0a1
+    PK11_FreeSlot(slot);
f8a0a1
+
f8a0a1
+  return token;
f8a0a1
+}
f8a0a1
+
f8a0a1
+static PK11Context *rc4InitContext(const Guchar *key, int keyLen) {
f8a0a1
+  CK_MECHANISM_TYPE  cipherMech = CKM_RC4;
f8a0a1
+  PK11SlotInfo      *slot = nullptr;
f8a0a1
+  PK11SymKey        *symKey = nullptr;
f8a0a1
+  SECItem           *secParam = nullptr;
f8a0a1
+  PK11Context       *context = nullptr;
f8a0a1
+
f8a0a1
+  slot = PK11_GetBestSlot(cipherMech, nullptr);
f8a0a1
+  if (slot == nullptr) {
f8a0a1
+    error(errInternal, -1, "Unable to find security device (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  symKey = tokenizeKey(key, keyLen, cipherMech);
f8a0a1
+  if (symKey == nullptr) {
f8a0a1
+    error(errInternal, -1, "Failed to create token from key (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  secParam = PK11_ParamFromIV(cipherMech, nullptr);
f8a0a1
+  if (secParam == nullptr) {
f8a0a1
+    error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  context = PK11_CreateContextBySymKey(cipherMech,
f8a0a1
+                                       CKA_DECRYPT,
f8a0a1
+                                       symKey,
f8a0a1
+                                       secParam);
f8a0a1
+  if (context == nullptr) {
f8a0a1
+    error(errInternal, -1, "Failed to create context (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+  }
f8a0a1
+
f8a0a1
+err:
f8a0a1
+  if (secParam != nullptr)
f8a0a1
+    SECITEM_FreeItem(secParam, PR_TRUE);
f8a0a1
+
f8a0a1
+  if (symKey != nullptr)
f8a0a1
+    PK11_FreeSymKey(symKey);
f8a0a1
+
f8a0a1
+  if (slot != nullptr)
f8a0a1
+    PK11_FreeSlot(slot);
f8a0a1
+
f8a0a1
+  return context;
f8a0a1
+}
f8a0a1
+
f8a0a1
+static Guchar rc4DecryptByte(PK11Context *context, Guchar c) {
f8a0a1
+  Guchar outputChar = 0;
f8a0a1
+  SECStatus     retval;
f8a0a1
+  int           outputLength;
f8a0a1
+
f8a0a1
+  retval = PK11_CipherOp(context,
f8a0a1
+                         &outputChar,
f8a0a1
+                         &outputLength,
f8a0a1
+                         1,
f8a0a1
+                         &c,
f8a0a1
+                         1);
f8a0a1
+
f8a0a1
+  if (retval != SECSuccess) {
f8a0a1
+    error(errInternal, -1, "Failed to decrypt byte (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  return outputChar;
f8a0a1
+}
f8a0a1
+
f8a0a1
+#else
f8a0a1
+
f8a0a1
 static void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
f8a0a1
   Guchar index1, index2;
f8a0a1
   Guchar t;
f8a0a1
@@ -609,6 +997,8 @@ static Guchar rc4DecryptByte(Guchar *sta
f8a0a1
   return c ^ state[(tx + ty) % 256];
f8a0a1
 }
f8a0a1
 
f8a0a1
+#endif
f8a0a1
+
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
 // AES decryption
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
@@ -639,6 +1029,178 @@ static GBool aesReadBlock(Stream *str, G
f8a0a1
   }
f8a0a1
 }
f8a0a1
 
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+
f8a0a1
+static PK11Context *aesInitContext(Guchar *in, Guchar *objKey,
f8a0a1
+                                   int objKeyLength, GBool decrypt) {
f8a0a1
+  CK_MECHANISM_TYPE  cipherMech = CKM_AES_CBC;
f8a0a1
+  CK_ATTRIBUTE_TYPE  operationType = decrypt ? CKA_DECRYPT : CKA_ENCRYPT;
f8a0a1
+  PK11SlotInfo      *slot;
f8a0a1
+  PK11SymKey        *symKey = nullptr;
f8a0a1
+  SECItem           *secParam = nullptr;
f8a0a1
+  PK11Context       *context = nullptr;
f8a0a1
+  SECItem            ivItem;
f8a0a1
+
f8a0a1
+  slot = PK11_GetBestSlot(cipherMech, nullptr);
f8a0a1
+  if (slot == nullptr) {
f8a0a1
+    error(errInternal, -1, "Unable to find security device (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  symKey = tokenizeKey(objKey, objKeyLength, cipherMech);
f8a0a1
+  if (symKey == nullptr) {
f8a0a1
+    error(errInternal, -1, "Failed to create token from key (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  ivItem.type = siBuffer;
f8a0a1
+  ivItem.data = in;
f8a0a1
+  ivItem.len = 16;
f8a0a1
+
f8a0a1
+  secParam = PK11_ParamFromIV(cipherMech, &ivItem);
f8a0a1
+  if (secParam == nullptr) {
f8a0a1
+    error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  context = PK11_CreateContextBySymKey(cipherMech,
f8a0a1
+                                       operationType,
f8a0a1
+                                       symKey,
f8a0a1
+                                       secParam);
f8a0a1
+
f8a0a1
+err:
f8a0a1
+  if (secParam != nullptr)
f8a0a1
+    SECITEM_FreeItem(secParam, PR_TRUE);
f8a0a1
+
f8a0a1
+  if (symKey != nullptr)
f8a0a1
+    PK11_FreeSymKey(symKey);
f8a0a1
+
f8a0a1
+  if (slot != nullptr)
f8a0a1
+    PK11_FreeSlot(slot);
f8a0a1
+
f8a0a1
+  return context;
f8a0a1
+}
f8a0a1
+
f8a0a1
+static void aesEncryptBlock(DecryptAESState *s, Guchar *in) {
f8a0a1
+  SECStatus rv;
f8a0a1
+  int       outputLength;
f8a0a1
+
f8a0a1
+  rv = PK11_CipherOp(s->context,
f8a0a1
+                     s->buf,
f8a0a1
+                     &outputLength,
f8a0a1
+                     16,
f8a0a1
+                     in,
f8a0a1
+                     16);
f8a0a1
+
f8a0a1
+  if (rv != SECSuccess) {
f8a0a1
+    error(errInternal, -1, "Failed to encrypt input block (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  s->bufIdx = 0;
f8a0a1
+
f8a0a1
+err:
f8a0a1
+  return;
f8a0a1
+}
f8a0a1
+
f8a0a1
+static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) {
f8a0a1
+  SECStatus rv1;
f8a0a1
+  int       outputLen;
f8a0a1
+  int       n, i;
f8a0a1
+
f8a0a1
+  rv1 = PK11_CipherOp(s->context,
f8a0a1
+                      s->buf,
f8a0a1
+                      &outputLen,
f8a0a1
+                      16,
f8a0a1
+                      in,
f8a0a1
+                      16);
f8a0a1
+
f8a0a1
+  if (rv1 != SECSuccess) {
f8a0a1
+    error(errInternal, -1, "Failed to decrypt input block (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  s->bufIdx = 0;
f8a0a1
+  if (last) {
f8a0a1
+    n = s->buf[15];
f8a0a1
+    if (n < 1 || n > 16) { // this should never happen
f8a0a1
+      n = 16;
f8a0a1
+    }
f8a0a1
+    for (i = 15; i >= n; --i) {
f8a0a1
+      s->buf[i] = s->buf[i-n];
f8a0a1
+    }
f8a0a1
+    s->bufIdx = n;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+err:
f8a0a1
+  return;
f8a0a1
+}
f8a0a1
+
f8a0a1
+static void aes256EncryptBlock(DecryptAES256State *s, Guchar *in) {
f8a0a1
+  SECStatus rv;
f8a0a1
+  int       outputLength;
f8a0a1
+
f8a0a1
+  rv = PK11_CipherOp(s->context,
f8a0a1
+                     s->buf,
f8a0a1
+                     &outputLength,
f8a0a1
+                     16,
f8a0a1
+                     in,
f8a0a1
+                     16);
f8a0a1
+
f8a0a1
+  if (rv != SECSuccess) {
f8a0a1
+    error(errInternal, -1, "Failed to encrypt input block (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  s->bufIdx = 0;
f8a0a1
+
f8a0a1
+err:
f8a0a1
+  return;
f8a0a1
+}
f8a0a1
+
f8a0a1
+static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in,
f8a0a1
+                               GBool last) {
f8a0a1
+  SECStatus rv1;
f8a0a1
+  int       outputLen;
f8a0a1
+  int       n, i;
f8a0a1
+
f8a0a1
+  rv1 = PK11_CipherOp(s->context,
f8a0a1
+                      s->buf,
f8a0a1
+                      &outputLen,
f8a0a1
+                      16,
f8a0a1
+                      in,
f8a0a1
+                      16);
f8a0a1
+
f8a0a1
+  if (rv1 != SECSuccess) {
f8a0a1
+    error(errInternal, -1, "Failed to decrypt input block (err {0:d})",
f8a0a1
+          PR_GetError());
f8a0a1
+    goto err;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+  s->bufIdx = 0;
f8a0a1
+  if (last) {
f8a0a1
+    n = s->buf[15];
f8a0a1
+    if (n < 1 || n > 16) { // this should never happen
f8a0a1
+      n = 16;
f8a0a1
+    }
f8a0a1
+    for (i = 15; i >= n; --i) {
f8a0a1
+      s->buf[i] = s->buf[i-n];
f8a0a1
+    }
f8a0a1
+    s->bufIdx = n;
f8a0a1
+  }
f8a0a1
+
f8a0a1
+err:
f8a0a1
+  return;
f8a0a1
+}
f8a0a1
+
f8a0a1
+#else
f8a0a1
+
f8a0a1
 static const Guchar sbox[256] = {
f8a0a1
   0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
f8a0a1
   0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
f8a0a1
@@ -1121,10 +1683,33 @@ static void aes256DecryptBlock(DecryptAE
f8a0a1
   }
f8a0a1
 }
f8a0a1
 
f8a0a1
+#endif
f8a0a1
+
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
 // MD5 message digest
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
 
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+static void hashFunc(const Guchar *msg, int msgLen, Guchar *hash,
f8a0a1
+                     HASH_HashType type) {
f8a0a1
+  HASHContext *context;
f8a0a1
+  Guint hashLen = 0;
f8a0a1
+
f8a0a1
+  if (!initNSS())
f8a0a1
+    return;
f8a0a1
+
f8a0a1
+  context = HASH_Create(type);
f8a0a1
+  if (context == nullptr)
f8a0a1
+    return;
f8a0a1
+
f8a0a1
+  HASH_Begin(context);
f8a0a1
+  HASH_Update(context, msg, msgLen);
f8a0a1
+  HASH_End(context, hash, &hashLen, HASH_ResultLen(type));
f8a0a1
+  HASH_Destroy(context);
f8a0a1
+}
f8a0a1
+
f8a0a1
+#else
f8a0a1
+
f8a0a1
 // this works around a bug in older Sun compilers
f8a0a1
 static inline Gulong rotateLeft(Gulong x, int r) {
f8a0a1
   x &= 0xffffffff;
f8a0a1
@@ -1151,7 +1736,12 @@ static inline Gulong md5Round4(Gulong a,
f8a0a1
   return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s);
f8a0a1
 }
f8a0a1
 
f8a0a1
+#endif
f8a0a1
+
f8a0a1
 void md5(Guchar *msg, int msgLen, Guchar *digest) {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  hashFunc(msg, msgLen, digest, HASH_AlgMD5);
f8a0a1
+#else
f8a0a1
   Gulong x[16] = {};
f8a0a1
   Gulong a, b, c, d, aa, bb, cc, dd;
f8a0a1
   int n64;
f8a0a1
@@ -1296,12 +1886,14 @@ void md5(Guchar *msg, int msgLen, Guchar
f8a0a1
   digest[13] = (Guchar)((d >>= 8) & 0xff);
f8a0a1
   digest[14] = (Guchar)((d >>= 8) & 0xff);
f8a0a1
   digest[15] = (Guchar)((d >>= 8) & 0xff);
f8a0a1
+#endif
f8a0a1
 }
f8a0a1
 
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
 // SHA-256 hash
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
 
f8a0a1
+#ifndef ENABLE_NSS3
f8a0a1
 static Guint sha256K[64] = {
f8a0a1
   0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
f8a0a1
   0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
f8a0a1
@@ -1400,8 +1992,12 @@ static void sha256HashBlock(Guchar *blk,
f8a0a1
   H[6] += g;
f8a0a1
   H[7] += h;
f8a0a1
 }
f8a0a1
+#endif
f8a0a1
 
f8a0a1
 static void sha256(Guchar *msg, int msgLen, Guchar *hash) {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  hashFunc(msg, msgLen, hash, HASH_AlgSHA256);
f8a0a1
+#else
f8a0a1
   Guchar blk[64];
f8a0a1
   Guint H[8];
f8a0a1
   int blkLen, i;
f8a0a1
@@ -1453,7 +2049,10 @@ static void sha256(Guchar *msg, int msgL
f8a0a1
     hash[i*4 + 2] = (Guchar)(H[i] >> 8);
f8a0a1
     hash[i*4 + 3] = (Guchar)H[i];
f8a0a1
   }
f8a0a1
+#endif
f8a0a1
 }
f8a0a1
+
f8a0a1
+#ifndef ENABLE_NSS3
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
 // SHA-512 hash (see FIPS 180-4)
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
@@ -1557,8 +2156,12 @@ static void sha512HashBlock(Guchar *blk,
f8a0a1
   H[6] += g;
f8a0a1
   H[7] += h;
f8a0a1
 }
f8a0a1
+#endif
f8a0a1
 
f8a0a1
 static void sha512(Guchar *msg, int msgLen, Guchar *hash) {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  hashFunc(msg, msgLen, hash, HASH_AlgSHA512);
f8a0a1
+#else
f8a0a1
   Guchar blk[128];
f8a0a1
   uint64_t H[8];
f8a0a1
   int blkLen = 0, i;
f8a0a1
@@ -1622,6 +2225,7 @@ static void sha512(Guchar *msg, int msgL
f8a0a1
     hash[i*8 + 6] = (Guchar)(H[i] >> 8);
f8a0a1
     hash[i*8 + 7] = (Guchar)H[i];
f8a0a1
   }
f8a0a1
+#endif
f8a0a1
 }
f8a0a1
 
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
@@ -1631,6 +2235,9 @@ static void sha512(Guchar *msg, int msgL
f8a0a1
 //1.Initial hash value is different.
f8a0a1
 //2.A 384 bit message digest is obtained by truncating the final hash value.
f8a0a1
 static void sha384(Guchar *msg, int msgLen, Guchar *hash) {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  hashFunc(msg, msgLen, hash, HASH_AlgSHA384);
f8a0a1
+#else
f8a0a1
   Guchar blk[128];
f8a0a1
   uint64_t H[8];
f8a0a1
   int blkLen, i;
f8a0a1
@@ -1696,6 +2303,7 @@ static void sha384(Guchar *msg, int msgL
f8a0a1
     hash[i*8 + 6] = (Guchar)(H[i] >> 8);
f8a0a1
     hash[i*8 + 7] = (Guchar)H[i];
f8a0a1
   }
f8a0a1
+#endif
f8a0a1
 }
f8a0a1
 
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
@@ -1725,7 +2333,8 @@ static void revision6Hash(GooString *inp
f8a0a1
     //a.make the string K1
f8a0a1
     memcpy(K1, inputPassword, inputPasswordLength);
f8a0a1
     memcpy(K1 + inputPasswordLength, K , KLength);
f8a0a1
-    memcpy(K1 + inputPasswordLength + KLength, userKey, userKeyLength);
f8a0a1
+    if (userKey)
f8a0a1
+      memcpy(K1 + inputPasswordLength + KLength, userKey, userKeyLength);
f8a0a1
     for(int i = 1; i < 64 ; ++i) {
f8a0a1
       memcpy(K1 + (i * sequenceLength),K1,sequenceLength);
f8a0a1
     }
f8a0a1
@@ -1735,7 +2344,11 @@ static void revision6Hash(GooString *inp
f8a0a1
     memcpy(state.buf, state.cbc, 16); // Copy CBC IV to buf
f8a0a1
     state.bufIdx = 0;
f8a0a1
     state.paddingReached = gFalse;
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    state.context = aesInitContext(state.cbc, aesKey, 16, gFalse);
f8a0a1
+#else
f8a0a1
     aesKeyExpansion(&state,aesKey,16,gFalse);
f8a0a1
+#endif
f8a0a1
 
f8a0a1
     for(int i = 0; i < (4 * sequenceLength); i++) {
f8a0a1
       aesEncryptBlock(&state,K1 + (16 * i));
f8a0a1
@@ -1776,6 +2389,9 @@ static void revision6Hash(GooString *inp
f8a0a1
       sha512(E, totalLength, K);
f8a0a1
     }
f8a0a1
     rounds++;
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+    PK11_DestroyContext(state.context, PR_TRUE);
f8a0a1
+#endif
f8a0a1
   }
f8a0a1
   // the first 32 bytes of the final K are the output of the function.
f8a0a1
 }
f8a0a1
diff --git a/poppler/Decrypt.h b/poppler/Decrypt.h
f8a0a1
index d4667c8c..16fa9830 100644
f8a0a1
--- a/poppler/Decrypt.h
f8a0a1
+++ b/poppler/Decrypt.h
f8a0a1
@@ -31,6 +32,9 @@
f8a0a1
 #include "goo/GooString.h"
f8a0a1
 #include "Object.h"
f8a0a1
 #include "Stream.h"
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+#include <pk11pub.h>
f8a0a1
+#endif
f8a0a1
 
f8a0a1
 //------------------------------------------------------------------------
f8a0a1
 // Decrypt
f8a0a1
@@ -73,13 +77,21 @@ private:
f8a0a1
  * previous output is kept in buf. The paddingReached field is only used in
f8a0a1
  * case of encryption. */
f8a0a1
 struct DecryptRC4State {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  PK11Context *context;
f8a0a1
+#else
f8a0a1
   Guchar state[256];
f8a0a1
   Guchar x, y;
f8a0a1
+#endif
f8a0a1
 };
f8a0a1
 
f8a0a1
 struct DecryptAESState {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  PK11Context *context;
f8a0a1
+#else
f8a0a1
   Guint w[44];
f8a0a1
   Guchar state[16];
f8a0a1
+#endif
f8a0a1
   Guchar cbc[16];
f8a0a1
   Guchar buf[16];
f8a0a1
   GBool paddingReached; // encryption only
f8a0a1
@@ -87,8 +99,12 @@ struct DecryptAESState {
f8a0a1
 };
f8a0a1
 
f8a0a1
 struct DecryptAES256State {
f8a0a1
+#ifdef ENABLE_NSS3
f8a0a1
+  PK11Context *context;
f8a0a1
+#else
f8a0a1
   Guint w[60];
f8a0a1
   Guchar state[16];
f8a0a1
+#endif
f8a0a1
   Guchar cbc[16];
f8a0a1
   Guchar buf[16];
f8a0a1
   GBool paddingReached; // encryption only
f8a0a1
diff --git a/poppler/poppler-config.h.cmake b/poppler/poppler-config.h.cmake
f8a0a1
index f0a5a1a0..dcaade6f 100644
f8a0a1
--- a/poppler/poppler-config.h.cmake
f8a0a1
+++ b/poppler/poppler-config.h.cmake
f8a0a1
@@ -115,6 +115,12 @@
f8a0a1
 #cmakedefine USE_CMS 1
f8a0a1
 #endif
f8a0a1
 
f8a0a1
+/* Build against libnss3 for digital signature validation and
f8a0a1
+   implementation of encryption/decryption. */
f8a0a1
+#ifndef ENABLE_NSS3
f8a0a1
+#cmakedefine ENABLE_NSS3 1
f8a0a1
+#endif
f8a0a1
+
f8a0a1
 // Also, there are preprocessor symbols in the header files
f8a0a1
 // that are used but never defined when building poppler using configure
f8a0a1
 // or cmake: DISABLE_OUTLINE, DEBUG_MEM,
f8a0a1
--- poppler-0.66.0/CMakeLists.txt
f8a0a1
+++ poppler-0.66.0/CMakeLists.txt
f8a0a1
@@ -490,7 +490,7 @@ add_library(poppler STATIC ${poppler_SRC
f8a0a1
 else()
f8a0a1
 add_library(poppler ${poppler_SRCS})
f8a0a1
 endif()
f8a0a1
-set_target_properties(poppler PROPERTIES VERSION 77.0.0 SOVERSION 77)
f8a0a1
+set_target_properties(poppler PROPERTIES VERSION 78.0.0 SOVERSION 78)
f8a0a1
 if(MINGW)
f8a0a1
     get_target_property(POPPLER_SOVERSION poppler SOVERSION)
f8a0a1
     set_target_properties(poppler PROPERTIES SUFFIX "-${POPPLER_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}")