387d7a
diff --git a/config.h.cmake b/config.h.cmake
387d7a
index 7989cbfb..6f5e147e 100644
387d7a
--- a/config.h.cmake
387d7a
+++ b/config.h.cmake
387d7a
@@ -24,9 +24,6 @@
387d7a
 /* Use zlib instead of builtin zlib decoder to uncompress flate streams. */
387d7a
 #cmakedefine ENABLE_ZLIB_UNCOMPRESS 1
387d7a
 
387d7a
-/* Build against libnss3 for digital signature validation */
387d7a
-#cmakedefine ENABLE_NSS3 1
387d7a
-
387d7a
 /* Use cairo for rendering. */
387d7a
 #cmakedefine HAVE_CAIRO 1
387d7a
 
387d7a
diff --git a/poppler/Decrypt.cc b/poppler/Decrypt.cc
387d7a
index 16476f4f..9f4adda3 100644
387d7a
--- a/poppler/Decrypt.cc
387d7a
+++ b/poppler/Decrypt.cc
387d7a
@@ -39,17 +39,33 @@
387d7a
 #include "goo/grandom.h"
387d7a
 #include "Decrypt.h"
387d7a
 #include "Error.h"
387d7a
-
387d7a
+#ifdef ENABLE_NSS3
387d7a
+#include <nss.h>
387d7a
+#include <prerror.h>
387d7a
+#include <sechash.h>
387d7a
+#endif
387d7a
+
387d7a
+#ifdef ENABLE_NSS3
387d7a
+static PK11Context *rc4InitContext(const unsigned char *key, int keyLen);
387d7a
+static unsigned char rc4DecryptByte(PK11Context *context, unsigned char c);
387d7a
+#else
387d7a
 static void rc4InitKey(const unsigned char *key, int keyLen, unsigned char *state);
387d7a
 static unsigned char rc4DecryptByte(unsigned char *state, unsigned char *x, unsigned char *y, unsigned char c);
387d7a
+#endif
387d7a
 
387d7a
 static bool aesReadBlock(Stream *str, unsigned char *in, bool addPadding);
387d7a
 
387d7a
+#ifdef ENABLE_NSS3
387d7a
+static PK11Context *aesInitContext(unsigned char *in, unsigned char *objKey, int objKeyLength, bool decrypt);
387d7a
+#else
387d7a
 static void aesKeyExpansion(DecryptAESState *s, const unsigned char *objKey, int objKeyLen, bool decrypt);
387d7a
+#endif
387d7a
 static void aesEncryptBlock(DecryptAESState *s, const unsigned char *in);
387d7a
 static void aesDecryptBlock(DecryptAESState *s, const unsigned char *in, bool last);
387d7a
 
387d7a
+#ifndef ENABLE_NSS3
387d7a
 static void aes256KeyExpansion(DecryptAES256State *s, const unsigned char *objKey, int objKeyLen, bool decrypt);
387d7a
+#endif
387d7a
 static void aes256EncryptBlock(DecryptAES256State *s, const unsigned char *in);
387d7a
 static void aes256DecryptBlock(DecryptAES256State *s, const unsigned char *in, bool last);
387d7a
 
387d7a
@@ -70,6 +86,31 @@ static const unsigned char passwordPad[32] = {
387d7a
 // Decrypt
387d7a
 //------------------------------------------------------------------------
387d7a
 
387d7a
+#ifdef ENABLE_NSS3
387d7a
+static void shutdownNSS()
387d7a
+{
387d7a
+    if (NSS_Shutdown() != SECSuccess) {
387d7a
+        error(errInternal, -1, "NSS shutdown failed: {0:s}",
387d7a
+              PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
387d7a
+    }
387d7a
+}
387d7a
+
387d7a
+static bool initNSS() {
387d7a
+    if (NSS_IsInitialized()) {
387d7a
+        return true;
387d7a
+    } else {
387d7a
+        if (NSS_NoDB_Init(".") != SECSuccess) {
387d7a
+            error(errInternal, -1, "NSS initialization failed: {0:s}",
387d7a
+                  PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
387d7a
+            return false;
387d7a
+        } else {
387d7a
+            atexit(shutdownNSS);
387d7a
+            return true;
387d7a
+        }
387d7a
+    }
387d7a
+}
387d7a
+#endif
387d7a
+
387d7a
 bool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength, const GooString *ownerKey, const GooString *userKey, const GooString *ownerEnc, const GooString *userEnc, int permissions, const GooString *fileID,
387d7a
                           const GooString *ownerPassword, const GooString *userPassword, unsigned char *fileKey, bool encryptMetadata, bool *ownerPasswordOk)
387d7a
 {
387d7a
@@ -80,13 +121,21 @@ bool Decrypt::makeFileKey(int encVersio
387d7a
     DecryptAES256State state;
387d7a
     unsigned char test[127 + 56], test2[32];
387d7a
     GooString *userPassword2;
387d7a
-    unsigned char fState[256];
387d7a
     unsigned char tmpKey[16];
387d7a
-    unsigned char fx, fy;
387d7a
     int len, i, j;
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    PK11Context *rc4Context;
387d7a
+#else
387d7a
+    unsigned char fState[256];
387d7a
+    unsigned char fx, fy;
387d7a
+#endif
387d7a
 
387d7a
     *ownerPasswordOk = false;
387d7a
 
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    initNSS();
387d7a
+#endif
387d7a
+
387d7a
     if (encRevision == 5 || encRevision == 6) {
387d7a
 
387d7a
         // check the owner password
387d7a
@@ -115,14 +164,26 @@ bool Decrypt::makeFileKey(int encVersio
387d7a
                     // test contains the initial SHA-256 hash input K.
387d7a
                     revision6Hash(ownerPassword, test, userKey->c_str());
387d7a
                 }
387d7a
+#ifndef ENABLE_NSS3
387d7a
                 aes256KeyExpansion(&state, test, 32, true);
387d7a
+#endif
387d7a
                 for (i = 0; i < 16; ++i) {
387d7a
                     state.cbc[i] = 0;
387d7a
                 }
387d7a
+#ifdef ENABLE_NSS3
387d7a
+                state.context = aesInitContext(state.cbc, test, 32, true);
387d7a
+                if (state.context) {
387d7a
+#endif
387d7a
                 aes256DecryptBlock(&state, (unsigned char *)ownerEnc->c_str(), false);
387d7a
                 memcpy(fileKey, state.buf, 16);
387d7a
                 aes256DecryptBlock(&state, (unsigned char *)ownerEnc->c_str() + 16, false);
387d7a
                 memcpy(fileKey + 16, state.buf, 16);
387d7a
+#ifdef ENABLE_NSS3
387d7a
+                PK11_DestroyContext(state.context, PR_TRUE);
387d7a
+                } else {
387d7a
+                    return false;
387d7a
+                }
387d7a
+#endif
387d7a
 
387d7a
                 *ownerPasswordOk = true;
387d7a
                 return true;
387d7a
@@ -156,14 +217,26 @@ bool Decrypt::makeFileKey(int encVersio
387d7a
                     // user key is not used in computing intermediate user key.
387d7a
                     revision6Hash(userPassword, test, nullptr);
387d7a
                 }
387d7a
+#ifndef ENABLE_NSS3
387d7a
                 aes256KeyExpansion(&state, test, 32, true);
387d7a
+#endif
387d7a
                 for (i = 0; i < 16; ++i) {
387d7a
                     state.cbc[i] = 0;
387d7a
                 }
387d7a
+#ifdef ENABLE_NSS3
387d7a
+                state.context = aesInitContext(state.cbc, test, 32, true);
387d7a
+                if (state.context) {
387d7a
+#endif
387d7a
                 aes256DecryptBlock(&state, (unsigned char *)userEnc->c_str(), false);
387d7a
                 memcpy(fileKey, state.buf, 16);
387d7a
                 aes256DecryptBlock(&state, (unsigned char *)userEnc->c_str() + 16, false);
387d7a
                 memcpy(fileKey + 16, state.buf, 16);
387d7a
+#ifdef ENABLE_NSS3
387d7a
+                PK11_DestroyContext(state.context, PR_TRUE);
387d7a
+                } else {
387d7a
+                    return false;
387d7a
+                }
387d7a
+#endif
387d7a
 
387d7a
                 return true;
387d7a
             }
387d7a
@@ -189,22 +262,41 @@ bool Decrypt::makeFileKey(int encVersio
387d7a
                 }
387d7a
             }
387d7a
             if (encRevision == 2) {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+                rc4Context = rc4InitContext(test, keyLength);
387d7a
+                if (rc4Context) {
387d7a
+                    for (i = 0; i < 32; ++i)
387d7a
+                        test2[i] = rc4DecryptByte(rc4Context, ownerKey->getChar(i));
387d7a
+                    PK11_DestroyContext(rc4Context, PR_TRUE);
387d7a
+                }
387d7a
+#else
387d7a
                 rc4InitKey(test, keyLength, fState);
387d7a
                 fx = fy = 0;
387d7a
                 for (i = 0; i < 32; ++i) {
387d7a
                     test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
387d7a
                 }
387d7a
+#endif
387d7a
             } else {
387d7a
                 memcpy(test2, ownerKey->c_str(), 32);
387d7a
                 for (i = 19; i >= 0; --i) {
387d7a
                     for (j = 0; j < keyLength; ++j) {
387d7a
                         tmpKey[j] = test[j] ^ i;
387d7a
                     }
387d7a
+#ifdef ENABLE_NSS3
387d7a
+                    rc4Context = rc4InitContext(tmpKey, keyLength);
387d7a
+                    if (rc4Context) {
387d7a
+                        for (j = 0; j < 32; ++j) {
387d7a
+                            test2[j] = rc4DecryptByte(rc4Context, test2[j]);
387d7a
+                        }
387d7a
+                        PK11_DestroyContext(rc4Context, PR_TRUE);
387d7a
+                    }
387d7a
+#else
387d7a
                     rc4InitKey(tmpKey, keyLength, fState);
387d7a
                     fx = fy = 0;
387d7a
                     for (j = 0; j < 32; ++j) {
387d7a
                         test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
387d7a
                     }
387d7a
+#endif
387d7a
                 }
387d7a
             }
387d7a
             userPassword2 = new GooString((char *)test2, 32);
387d7a
@@ -232,11 +324,15 @@ bool Decrypt::makeFileKey2(int encVersi
387d7a
 {
387d7a
     unsigned char *buf;
387d7a
     unsigned char test[32];
387d7a
-    unsigned char fState[256];
387d7a
     unsigned char tmpKey[16];
387d7a
-    unsigned char fx, fy;
387d7a
     int len, i, j;
387d7a
-    bool ok;
387d7a
+    bool ok = true;
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    PK11Context *rc4Context;
387d7a
+#else
387d7a
+    unsigned char fState[256];
387d7a
+    unsigned char fx, fy;
387d7a
+#endif
387d7a
 
387d7a
     // generate file key
387d7a
     buf = (unsigned char *)gmalloc(72 + fileID->getLength());
387d7a
@@ -273,28 +369,52 @@ bool Decrypt::makeFileKey2(int encVersi
387d7a
 
387d7a
     // test user password
387d7a
     if (encRevision == 2) {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        rc4Context = rc4InitContext(fileKey, keyLength);
387d7a
+        if (rc4Context) {
387d7a
+            for (i = 0; i < 32; ++i)
387d7a
+                test[i] = rc4DecryptByte(rc4Context, userKey->getChar(i));
387d7a
+            PK11_DestroyContext(rc4Context, PR_TRUE);
387d7a
+        } else {
387d7a
+            ok = false;
387d7a
+        }
387d7a
+#else
387d7a
         rc4InitKey(fileKey, keyLength, fState);
387d7a
         fx = fy = 0;
387d7a
         for (i = 0; i < 32; ++i) {
387d7a
             test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i));
387d7a
         }
387d7a
-        ok = memcmp(test, passwordPad, 32) == 0;
387d7a
+#endif
387d7a
+        if (ok)
387d7a
+            ok = memcmp(test, passwordPad, 32) == 0;
387d7a
     } else if (encRevision == 3) {
387d7a
         memcpy(test, userKey->c_str(), 32);
387d7a
         for (i = 19; i >= 0; --i) {
387d7a
             for (j = 0; j < keyLength; ++j) {
387d7a
                 tmpKey[j] = fileKey[j] ^ i;
387d7a
             }
387d7a
+#ifdef ENABLE_NSS3
387d7a
+            rc4Context = rc4InitContext(tmpKey, keyLength);
387d7a
+            if (rc4Context) {
387d7a
+                for (j = 0; j < 32; ++j)
387d7a
+                    test[j] = rc4DecryptByte(rc4Context, test[j]);
387d7a
+                PK11_DestroyContext(rc4Context, PR_TRUE);
387d7a
+            } else {
387d7a
+                ok = false;
387d7a
+            }
387d7a
+#else
387d7a
             rc4InitKey(tmpKey, keyLength, fState);
387d7a
             fx = fy = 0;
387d7a
             for (j = 0; j < 32; ++j) {
387d7a
                 test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]);
387d7a
             }
387d7a
+#endif
387d7a
         }
387d7a
         memcpy(buf, passwordPad, 32);
387d7a
         memcpy(buf + 32, fileID->c_str(), fileID->getLength());
387d7a
         md5(buf, 32 + fileID->getLength(), buf);
387d7a
-        ok = memcmp(test, buf, 16) == 0;
387d7a
+        if (ok)
387d7a
+            ok = memcmp(test, buf, 16) == 0;
387d7a
     } else {
387d7a
         ok = false;
387d7a
     }
387d7a
@@ -334,6 +454,9 @@ BaseCryptStream::BaseCryptStream(Stream
387d7a
         if ((objKeyLength = keyLength + 5) > 16) {
387d7a
             objKeyLength = 16;
387d7a
         }
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        state.rc4.context = nullptr;
387d7a
+#endif
387d7a
         break;
387d7a
     case cryptAES:
387d7a
         objKey[keyLength] = refA.num & 0xff;
387d7a
@@ -349,9 +472,15 @@ BaseCryptStream::BaseCryptStream(Stream
387d7a
         if ((objKeyLength = keyLength + 5) > 16) {
387d7a
             objKeyLength = 16;
387d7a
         }
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        state.aes.context = nullptr;
387d7a
+#endif
387d7a
         break;
387d7a
     case cryptAES256:
387d7a
         objKeyLength = keyLength;
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        state.aes256.context = nullptr;
387d7a
+#endif
387d7a
         break;
387d7a
     case cryptNone:
387d7a
         break;
387d7a
@@ -359,10 +488,33 @@ BaseCryptStream::BaseCryptStream(Stream
387d7a
     charactersRead = 0;
387d7a
     nextCharBuff = EOF;
387d7a
     autoDelete = true;
387d7a
+
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    initNSS();
387d7a
+#endif
387d7a
 }
387d7a
 
387d7a
 BaseCryptStream::~BaseCryptStream()
387d7a
 {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    switch (algo) {
387d7a
+    case cryptRC4:
387d7a
+        if (state.rc4.context)
387d7a
+            PK11_DestroyContext(state.rc4.context, PR_TRUE);
387d7a
+        break;
387d7a
+    case cryptAES:
387d7a
+        if (state.aes.context)
387d7a
+            PK11_DestroyContext(state.aes.context, PR_TRUE);
387d7a
+        break;
387d7a
+    case cryptAES256:
387d7a
+        if (state.aes256.context)
387d7a
+            PK11_DestroyContext(state.aes256.context, PR_TRUE);
387d7a
+        break;
387d7a
+    default:
387d7a
+        break;
387d7a
+    }
387d7a
+#endif
387d7a
+
387d7a
     if (autoDelete) {
387d7a
         delete str;
387d7a
     }
387d7a
@@ -424,18 +576,40 @@ void EncryptStream::reset() {
387d7a
 
387d7a
     switch (algo) {
387d7a
     case cryptRC4:
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        if (state.rc4.context)
387d7a
+            PK11_DestroyContext(state.rc4.context, PR_TRUE);
387d7a
+        state.rc4.context = rc4InitContext(objKey, objKeyLength);
387d7a
+#else
387d7a
         state.rc4.x = state.rc4.y = 0;
387d7a
         rc4InitKey(objKey, objKeyLength, state.rc4.state);
387d7a
+#endif
387d7a
         break;
387d7a
     case cryptAES:
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf
387d7a
+        if (state.aes.context)
387d7a
+            PK11_DestroyContext(state.aes.context, PR_TRUE);
387d7a
+        state.aes.context = aesInitContext(state.aes.cbc, objKey, objKeyLength,
387d7a
+                                           false);
387d7a
+#else
387d7a
         aesKeyExpansion(&state.aes, objKey, objKeyLength, false);
387d7a
         memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf
387d7a
+#endif
387d7a
         state.aes.bufIdx = 0;
387d7a
         state.aes.paddingReached = false;
387d7a
         break;
387d7a
     case cryptAES256:
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf
387d7a
+        if (state.aes256.context)
387d7a
+            PK11_DestroyContext(state.aes256.context, PR_TRUE);
387d7a
+        state.aes256.context = aesInitContext(state.aes256.cbc, objKey, objKeyLength,
387d7a
+                                              false);
387d7a
+#else
387d7a
         aes256KeyExpansion(&state.aes256, objKey, objKeyLength, false);
387d7a
         memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf
387d7a
+#endif
387d7a
         state.aes256.bufIdx = 0;
387d7a
         state.aes256.paddingReached = false;
387d7a
         break;
387d7a
@@ -456,7 +630,11 @@ int EncryptStream::lookChar() {
387d7a
     case cryptRC4:
387d7a
         if ((c = str->getChar()) != EOF) {
387d7a
             // RC4 is XOR-based: the decryption algorithm works for encryption too
387d7a
+#ifdef ENABLE_NSS3
387d7a
+            c = rc4DecryptByte(state.rc4.context, (unsigned char)c);
387d7a
+#else
387d7a
             c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (unsigned char)c);
387d7a
+#endif
387d7a
         }
387d7a
         break;
387d7a
     case cryptAES:
387d7a
@@ -506,21 +684,47 @@ void DecryptStream::reset() {
387d7a
 
387d7a
     switch (algo) {
387d7a
     case cryptRC4:
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        if (state.rc4.context)
387d7a
+            PK11_DestroyContext(state.rc4.context, PR_TRUE);
387d7a
+        state.rc4.context = rc4InitContext(objKey, objKeyLength);
387d7a
+#else
387d7a
         state.rc4.x = state.rc4.y = 0;
387d7a
         rc4InitKey(objKey, objKeyLength, state.rc4.state);
387d7a
+#endif
387d7a
         break;
387d7a
     case cryptAES:
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        if (state.aes.context)
387d7a
+            PK11_DestroyContext(state.aes.context, PR_TRUE);
387d7a
+        for (i = 0; i < 16; ++i) {
387d7a
+            state.aes.cbc[i] = str->getChar();
387d7a
+        }
387d7a
+        state.aes.context = aesInitContext(state.aes.cbc, objKey, objKeyLength,
387d7a
+                                           true);
387d7a
+#else
387d7a
         aesKeyExpansion(&state.aes, objKey, objKeyLength, true);
387d7a
         for (i = 0; i < 16; ++i) {
387d7a
             state.aes.cbc[i] = str->getChar();
387d7a
         }
387d7a
+#endif
387d7a
         state.aes.bufIdx = 16;
387d7a
         break;
387d7a
     case cryptAES256:
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        if (state.aes256.context)
387d7a
+            PK11_DestroyContext(state.aes256.context, PR_TRUE);
387d7a
+        for (i = 0; i < 16; ++i) {
387d7a
+            state.aes256.cbc[i] = str->getChar();
387d7a
+        }
387d7a
+        state.aes256.context = aesInitContext(state.aes256.cbc, objKey, objKeyLength,
387d7a
+                                              true);
387d7a
+#else
387d7a
         aes256KeyExpansion(&state.aes256, objKey, objKeyLength, true);
387d7a
         for (i = 0; i < 16; ++i) {
387d7a
             state.aes256.cbc[i] = str->getChar();
387d7a
         }
387d7a
+#endif
387d7a
         state.aes256.bufIdx = 16;
387d7a
         break;
387d7a
     case cryptNone:
387d7a
@@ -539,10 +743,21 @@ int DecryptStream::lookChar() {
387d7a
     switch (algo) {
387d7a
     case cryptRC4:
387d7a
         if ((c = str->getChar()) != EOF) {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+            if (unlikely(state.rc4.context == nullptr))
387d7a
+                c = EOF;
387d7a
+            else
387d7a
+                c = rc4DecryptByte(state.rc4.context, (unsigned char)c);
387d7a
+#else
387d7a
             c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (unsigned char)c);
387d7a
+#endif
387d7a
         }
387d7a
         break;
387d7a
     case cryptAES:
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        if (unlikely(state.aes.context == nullptr))
387d7a
+            break;
387d7a
+#endif
387d7a
         if (state.aes.bufIdx == 16) {
387d7a
             if (aesReadBlock(str, in, false)) {
387d7a
                 aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
387d7a
@@ -555,6 +770,10 @@ int DecryptStream::lookChar() {
387d7a
         }
387d7a
         break;
387d7a
     case cryptAES256:
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        if (unlikely(state.aes256.context == nullptr))
387d7a
+            break;
387d7a
+#endif
387d7a
         if (state.aes256.bufIdx == 16) {
387d7a
             if (aesReadBlock(str, in, false)) {
387d7a
                 aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
387d7a
@@ -576,7 +795,176 @@ int DecryptStream::lookChar() {
387d7a
 // RC4-compatible decryption
387d7a
 //------------------------------------------------------------------------
387d7a
 
387d7a
+#ifdef ENABLE_NSS3
387d7a
+/*
387d7a
+ * This function turns given key into token key (compared to a session key
387d7a
+ * which is prohibited in FIPS mode).
387d7a
+ */
387d7a
+static PK11SymKey *tokenizeKey(const unsigned char *key, int keyLen,
387d7a
+                               CK_ATTRIBUTE_TYPE operation) {
387d7a
+    CK_MECHANISM_TYPE  cipherMech = CKM_AES_CBC_PAD;
387d7a
+    PK11SlotInfo      *slot;
387d7a
+    PK11SymKey        *wrappingKey = nullptr;
387d7a
+    PK11SymKey        *symKey = nullptr;
387d7a
+    PK11SymKey        *token = nullptr;
387d7a
+    SECStatus          retval;
387d7a
+    SECItem           *secParam = nullptr;
387d7a
+    SECItem            ivItem, wrappedKey;
387d7a
+    unsigned char      output[48];   // Buffer to hold 256 bit key + padding
387d7a
+    unsigned char      iv[16];       // Initialization vector for AES
387d7a
+    unsigned int       outputLength;
387d7a
+    int                i;
387d7a
+
387d7a
+    slot = PK11_GetBestSlot(CKM_AES_KEY_GEN, nullptr);
387d7a
+    if (slot == nullptr) {
387d7a
+        error(errInternal, -1, "Unable to find security device (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    // Generate random key for wrapping of given key by AES-256
387d7a
+    wrappingKey = PK11_KeyGen(slot, CKM_AES_KEY_GEN, nullptr, 32, nullptr);
387d7a
+    if (wrappingKey == nullptr) {
387d7a
+        error(errInternal, -1, "Failed to generate wrapping key (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    for (i = 0; i < 16; i++)
387d7a
+        iv[i] = i;
387d7a
+
387d7a
+    ivItem.type = siBuffer;
387d7a
+    ivItem.data = iv;
387d7a
+    ivItem.len = 16;
387d7a
+
387d7a
+    secParam = PK11_ParamFromIV(cipherMech, &ivItem);
387d7a
+    if (secParam == nullptr) {
387d7a
+        error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    // Encrypt given key
387d7a
+    retval = PK11_Encrypt(wrappingKey,
387d7a
+                          cipherMech,
387d7a
+                          secParam,
387d7a
+                          output,
387d7a
+                          &outputLength,
387d7a
+                          sizeof(output),
387d7a
+                          key,
387d7a
+                          keyLen);
387d7a
+    if (retval != SECSuccess) {
387d7a
+        error(errInternal, -1, "Failed to encrypt key (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+    }
387d7a
+
387d7a
+    wrappedKey.type = siBuffer;
387d7a
+    wrappedKey.data = output;
387d7a
+    wrappedKey.len = outputLength;
387d7a
+
387d7a
+    // Unwrap the wrapped key to token so it can be used in FIPS mode
387d7a
+    token = PK11_UnwrapSymKey(wrappingKey,
387d7a
+                              cipherMech,
387d7a
+                              &ivItem,
387d7a
+                              &wrappedKey,
387d7a
+                              operation,
387d7a
+                              CKA_UNWRAP,
387d7a
+                              keyLen);
387d7a
+
387d7a
+    if (token == nullptr) {
387d7a
+        error(errInternal, -1, "Failed to unwrap symmetric key (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+    }
387d7a
+
387d7a
+err:
387d7a
+    if (secParam != nullptr)
387d7a
+        SECITEM_FreeItem(secParam, PR_TRUE);
387d7a
+
387d7a
+    if (wrappingKey != nullptr)
387d7a
+        PK11_FreeSymKey(wrappingKey);
387d7a
+
387d7a
+    if (symKey != nullptr)
387d7a
+        PK11_FreeSymKey(symKey);
387d7a
+
387d7a
+    if (slot != nullptr)
387d7a
+        PK11_FreeSlot(slot);
387d7a
+
387d7a
+    return token;
387d7a
+}
387d7a
+
387d7a
+static PK11Context *rc4InitContext(const unsigned char *key, int keyLen) {
387d7a
+    CK_MECHANISM_TYPE  cipherMech = CKM_RC4;
387d7a
+    PK11SlotInfo      *slot = nullptr;
387d7a
+    PK11SymKey        *symKey = nullptr;
387d7a
+    SECItem           *secParam = nullptr;
387d7a
+    PK11Context       *context = nullptr;
387d7a
+
387d7a
+    slot = PK11_GetBestSlot(cipherMech, nullptr);
387d7a
+    if (slot == nullptr) {
387d7a
+        error(errInternal, -1, "Unable to find security device (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    symKey = tokenizeKey(key, keyLen, cipherMech);
387d7a
+    if (symKey == nullptr) {
387d7a
+        error(errInternal, -1, "Failed to create token from key (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    secParam = PK11_ParamFromIV(cipherMech, nullptr);
387d7a
+    if (secParam == nullptr) {
387d7a
+        error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    context = PK11_CreateContextBySymKey(cipherMech,
387d7a
+                                         CKA_DECRYPT,
387d7a
+                                         symKey,
387d7a
+                                         secParam);
387d7a
+    if (context == nullptr) {
387d7a
+        error(errInternal, -1, "Failed to create context (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+    }
387d7a
+
387d7a
+err:
387d7a
+    if (secParam != nullptr)
387d7a
+        SECITEM_FreeItem(secParam, PR_TRUE);
387d7a
+
387d7a
+    if (symKey != nullptr)
387d7a
+        PK11_FreeSymKey(symKey);
387d7a
+
387d7a
+    if (slot != nullptr)
387d7a
+        PK11_FreeSlot(slot);
387d7a
+
387d7a
+    return context;
387d7a
+}
387d7a
+
387d7a
+static unsigned char rc4DecryptByte(PK11Context *context, unsigned char c) {
387d7a
+    unsigned char outputChar = 0;
387d7a
+    SECStatus     retval;
387d7a
+    int           outputLength;
387d7a
+
387d7a
+    retval = PK11_CipherOp(context,
387d7a
+                           &outputChar,
387d7a
+                           &outputLength,
387d7a
+                           1,
387d7a
+                           &c,
387d7a
+                           1);
387d7a
+
387d7a
+    if (retval != SECSuccess) {
387d7a
+        error(errInternal, -1, "Failed to decrypt byte (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+    }
387d7a
+
387d7a
+    return outputChar;
387d7a
+}
387d7a
+
387d7a
+#else
387d7a
+
387d7a
 static void rc4InitKey(const unsigned char *key, int keyLen, unsigned char *state)
387d7a
 {
387d7a
     unsigned char index1, index2;
387d7a
     unsigned char t;
387d7a
@@ -609,6 +997,8 @@ static unsigned char rc4DecryptByte(unsigned char *sta
387d7a
     return c ^ state[(tx + ty) % 256];
387d7a
 }
387d7a
 
387d7a
+#endif
387d7a
+
387d7a
 //------------------------------------------------------------------------
387d7a
 // AES decryption
387d7a
 //------------------------------------------------------------------------
387d7a
@@ -639,6 +1029,178 @@ static bool aesReadBlock(Stream *str, G
387d7a
     }
387d7a
 }
387d7a
 
387d7a
+#ifdef ENABLE_NSS3
387d7a
+
387d7a
+static PK11Context *aesInitContext(unsigned char *in, unsigned char *objKey,
387d7a
+                                   int objKeyLength, bool decrypt) {
387d7a
+    CK_MECHANISM_TYPE  cipherMech = CKM_AES_CBC;
387d7a
+    CK_ATTRIBUTE_TYPE  operationType = decrypt ? CKA_DECRYPT : CKA_ENCRYPT;
387d7a
+    PK11SlotInfo      *slot;
387d7a
+    PK11SymKey        *symKey = nullptr;
387d7a
+    SECItem           *secParam = nullptr;
387d7a
+    PK11Context       *context = nullptr;
387d7a
+    SECItem            ivItem;
387d7a
+
387d7a
+    slot = PK11_GetBestSlot(cipherMech, nullptr);
387d7a
+    if (slot == nullptr) {
387d7a
+        error(errInternal, -1, "Unable to find security device (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    symKey = tokenizeKey(objKey, objKeyLength, cipherMech);
387d7a
+    if (symKey == nullptr) {
387d7a
+        error(errInternal, -1, "Failed to create token from key (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    ivItem.type = siBuffer;
387d7a
+    ivItem.data = in;
387d7a
+    ivItem.len = 16;
387d7a
+
387d7a
+    secParam = PK11_ParamFromIV(cipherMech, &ivItem);
387d7a
+    if (secParam == nullptr) {
387d7a
+        error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    context = PK11_CreateContextBySymKey(cipherMech,
387d7a
+                                         operationType,
387d7a
+                                         symKey,
387d7a
+                                         secParam);
387d7a
+
387d7a
+err:
387d7a
+    if (secParam != nullptr)
387d7a
+        SECITEM_FreeItem(secParam, PR_TRUE);
387d7a
+
387d7a
+    if (symKey != nullptr)
387d7a
+        PK11_FreeSymKey(symKey);
387d7a
+
387d7a
+    if (slot != nullptr)
387d7a
+        PK11_FreeSlot(slot);
387d7a
+
387d7a
+    return context;
387d7a
+}
387d7a
+
387d7a
+static void aesEncryptBlock(DecryptAESState *s, const unsigned char *in) {
387d7a
+    SECStatus rv;
387d7a
+    int       outputLength;
387d7a
+
387d7a
+    rv = PK11_CipherOp(s->context,
387d7a
+                       s->buf,
387d7a
+                       &outputLength,
387d7a
+                       16,
387d7a
+                       in,
387d7a
+                       16);
387d7a
+
387d7a
+    if (rv != SECSuccess) {
387d7a
+        error(errInternal, -1, "Failed to encrypt input block (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    s->bufIdx = 0;
387d7a
+
387d7a
+err:
387d7a
+    return;
387d7a
+}
387d7a
+
387d7a
+static void aesDecryptBlock(DecryptAESState *s, const unsigned char *in, bool last) {
387d7a
+    SECStatus rv1;
387d7a
+    int       outputLen;
387d7a
+    int       n, i;
387d7a
+
387d7a
+    rv1 = PK11_CipherOp(s->context,
387d7a
+                        s->buf,
387d7a
+                        &outputLen,
387d7a
+                        16,
387d7a
+                        in,
387d7a
+                        16);
387d7a
+
387d7a
+    if (rv1 != SECSuccess) {
387d7a
+        error(errInternal, -1, "Failed to decrypt input block (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    s->bufIdx = 0;
387d7a
+    if (last) {
387d7a
+        n = s->buf[15];
387d7a
+        if (n < 1 || n > 16) { // this should never happen
387d7a
+            n = 16;
387d7a
+        }
387d7a
+        for (i = 15; i >= n; --i) {
387d7a
+            s->buf[i] = s->buf[i-n];
387d7a
+        }
387d7a
+        s->bufIdx = n;
387d7a
+    }
387d7a
+
387d7a
+err:
387d7a
+    return;
387d7a
+}
387d7a
+
387d7a
+static void aes256EncryptBlock(DecryptAES256State *s, const unsigned char *in) {
387d7a
+    SECStatus rv;
387d7a
+    int       outputLength;
387d7a
+
387d7a
+    rv = PK11_CipherOp(s->context,
387d7a
+                       s->buf,
387d7a
+                       &outputLength,
387d7a
+                       16,
387d7a
+                       in,
387d7a
+                       16);
387d7a
+
387d7a
+    if (rv != SECSuccess) {
387d7a
+        error(errInternal, -1, "Failed to encrypt input block (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    s->bufIdx = 0;
387d7a
+
387d7a
+err:
387d7a
+    return;
387d7a
+}
387d7a
+
387d7a
+static void aes256DecryptBlock(DecryptAES256State *s, const unsigned char *in,
387d7a
+                               bool last) {
387d7a
+    SECStatus rv1;
387d7a
+    int       outputLen;
387d7a
+    int       n, i;
387d7a
+
387d7a
+    rv1 = PK11_CipherOp(s->context,
387d7a
+                        s->buf,
387d7a
+                        &outputLen,
387d7a
+                        16,
387d7a
+                        in,
387d7a
+                        16);
387d7a
+
387d7a
+    if (rv1 != SECSuccess) {
387d7a
+        error(errInternal, -1, "Failed to decrypt input block (err {0:d})",
387d7a
+              PR_GetError());
387d7a
+        goto err;
387d7a
+    }
387d7a
+
387d7a
+    s->bufIdx = 0;
387d7a
+    if (last) {
387d7a
+        n = s->buf[15];
387d7a
+        if (n < 1 || n > 16) { // this should never happen
387d7a
+            n = 16;
387d7a
+        }
387d7a
+        for (i = 15; i >= n; --i) {
387d7a
+            s->buf[i] = s->buf[i-n];
387d7a
+        }
387d7a
+        s->bufIdx = n;
387d7a
+    }
387d7a
+
387d7a
+err:
387d7a
+    return;
387d7a
+}
387d7a
+
387d7a
+#else
387d7a
+
387d7a
 static const unsigned char sbox[256] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
387d7a
                                          0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
387d7a
                                          0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
387d7a
@@ -1121,10 +1683,33 @@ static void aes256DecryptBlock(DecryptAE
387d7a
     }
387d7a
 }
387d7a
 
387d7a
+#endif
387d7a
+
387d7a
 //------------------------------------------------------------------------
387d7a
 // MD5 message digest
387d7a
 //------------------------------------------------------------------------
387d7a
 
387d7a
+#ifdef ENABLE_NSS3
387d7a
+static void hashFunc(const unsigned char *msg, int msgLen, unsigned char *hash,
387d7a
+                     HASH_HashType type) {
387d7a
+    HASHContext *context;
387d7a
+    unsigned int hashLen = 0;
387d7a
+
387d7a
+    if (!initNSS())
387d7a
+        return;
387d7a
+
387d7a
+    context = HASH_Create(type);
387d7a
+    if (context == nullptr)
387d7a
+        return;
387d7a
+
387d7a
+    HASH_Begin(context);
387d7a
+    HASH_Update(context, msg, msgLen);
387d7a
+    HASH_End(context, hash, &hashLen, HASH_ResultLen(type));
387d7a
+    HASH_Destroy(context);
387d7a
+}
387d7a
+
387d7a
+#else
387d7a
+
387d7a
 // this works around a bug in older Sun compilers
387d7a
 static inline unsigned long rotateLeft(unsigned long x, int r)
387d7a
 {
387d7a
@@ -1151,8 +1736,13 @@ static inline unsigned long md5Round4(unsigned long a,
387d7a
     state->digest[15] = (unsigned char)(state->d >> 24);
387d7a
 }
387d7a
 
387d7a
+#endif
387d7a
+
387d7a
 void md5(const unsigned char *msg, int msgLen, unsigned char *digest)
387d7a
 {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    hashFunc(msg, msgLen, digest, HASH_AlgMD5);
387d7a
+#else
387d7a
     if (msgLen < 0) {
387d7a
         return;
387d7a
     }
387d7a
@@ -1296,12 +1886,14 @@ void md5(unsigned char *msg, int msgLen, unsigned char
387d7a
     for (int i = 0; i < 16; ++i) {
387d7a
         digest[i] = state.digest[i];
387d7a
     }
387d7a
+#endif
387d7a
 }
387d7a
 
387d7a
 //------------------------------------------------------------------------
387d7a
 // SHA-256 hash
387d7a
 //------------------------------------------------------------------------
387d7a
 
387d7a
+#ifndef ENABLE_NSS3
387d7a
 static const unsigned int sha256K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
387d7a
                                           0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
387d7a
                                           0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
387d7a
@@ -1400,9 +1992,13 @@ static void sha256HashBlock(unsigned char *blk,
387d7a
     H[6] += g;
387d7a
     H[7] += h;
387d7a
 }
387d7a
+#endif
387d7a
 
387d7a
 static void sha256(unsigned char *msg, int msgLen, unsigned char *hash)
387d7a
 {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    hashFunc(msg, msgLen, hash, HASH_AlgSHA256);
387d7a
+#else
387d7a
     unsigned char blk[64];
387d7a
     unsigned int H[8];
387d7a
     int blkLen, i;
387d7a
@@ -1453,7 +2049,10 @@ static void sha256(unsigned char *msg, int msgL
387d7a
         hash[i * 4 + 2] = (unsigned char)(H[i] >> 8);
387d7a
         hash[i * 4 + 3] = (unsigned char)H[i];
387d7a
     }
387d7a
+#endif
387d7a
 }
387d7a
+
387d7a
+#ifndef ENABLE_NSS3
387d7a
 //------------------------------------------------------------------------
387d7a
 // SHA-512 hash (see FIPS 180-4)
387d7a
 //------------------------------------------------------------------------
387d7a
@@ -1557,9 +2156,13 @@ static void sha512HashBlock(unsigned char *blk,
387d7a
     H[6] += g;
387d7a
     H[7] += h;
387d7a
 }
387d7a
+#endif
387d7a
 
387d7a
 static void sha512(unsigned char *msg, int msgLen, unsigned char *hash)
387d7a
 {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    hashFunc(msg, msgLen, hash, HASH_AlgSHA512);
387d7a
+#else
387d7a
     unsigned char blk[128];
387d7a
     uint64_t H[8];
387d7a
     int blkLen = 0, i;
387d7a
@@ -1622,6 +2225,7 @@ static void sha512(unsigned char *msg, int msgL
387d7a
         hash[i * 8 + 6] = (unsigned char)(H[i] >> 8);
387d7a
         hash[i * 8 + 7] = (unsigned char)H[i];
387d7a
     }
387d7a
+#endif
387d7a
 }
387d7a
 
387d7a
 //------------------------------------------------------------------------
387d7a
@@ -1631,6 +2235,9 @@ static void sha512(unsigned char *msg, int msgL
387d7a
 // 2.A 384 bit message digest is obtained by truncating the final hash value.
387d7a
 static void sha384(unsigned char *msg, int msgLen, unsigned char *hash)
387d7a
 {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    hashFunc(msg, msgLen, hash, HASH_AlgSHA384);
387d7a
+#else
387d7a
     unsigned char blk[128];
387d7a
     uint64_t H[8];
387d7a
     int blkLen, i;
387d7a
@@ -1696,6 +2303,7 @@ static void sha384(unsigned char *msg, int msgL
387d7a
         hash[i * 8 + 6] = (unsigned char)(H[i] >> 8);
387d7a
         hash[i * 8 + 7] = (unsigned char)H[i];
387d7a
     }
387d7a
+#endif
387d7a
 }
387d7a
 
387d7a
 //------------------------------------------------------------------------
387d7a
@@ -1735,7 +2344,11 @@ static void revision6Hash(GooString *inp
387d7a
         memcpy(state.buf, state.cbc, 16); // Copy CBC IV to buf
387d7a
         state.bufIdx = 0;
387d7a
         state.paddingReached = false;
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        state.context = aesInitContext(state.cbc, aesKey, 16, false);
387d7a
+#else
387d7a
         aesKeyExpansion(&state, aesKey, 16, false);
387d7a
+#endif
387d7a
 
387d7a
         for (int i = 0; i < (4 * sequenceLength); i++) {
387d7a
             aesEncryptBlock(&state, K1 + (16 * i));
387d7a
@@ -1776,6 +2389,9 @@ static void revision6Hash(GooString *inp
387d7a
             sha512(E, totalLength, K);
387d7a
         }
387d7a
         rounds++;
387d7a
+#ifdef ENABLE_NSS3
387d7a
+        PK11_DestroyContext(state.context, PR_TRUE);
387d7a
+#endif
387d7a
     }
387d7a
     // the first 32 bytes of the final K are the output of the function.
387d7a
 }
387d7a
diff --git a/poppler/Decrypt.h b/poppler/Decrypt.h
387d7a
index d4667c8c..16fa9830 100644
387d7a
--- a/poppler/Decrypt.h
387d7a
+++ b/poppler/Decrypt.h
387d7a
@@ -31,6 +32,9 @@
387d7a
 #include "goo/GooString.h"
387d7a
 #include "Object.h"
387d7a
 #include "Stream.h"
387d7a
+#ifdef ENABLE_NSS3
387d7a
+#include <pk11pub.h>
387d7a
+#endif
387d7a
 
387d7a
 //------------------------------------------------------------------------
387d7a
 // Decrypt
387d7a
@@ -73,14 +77,22 @@ private:
387d7a
  * case of encryption. */
387d7a
 struct DecryptRC4State
387d7a
 {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    PK11Context *context;
387d7a
+#else
387d7a
     unsigned char state[256];
387d7a
     unsigned char x, y;
387d7a
+#endif
387d7a
 };
387d7a
 
387d7a
 struct DecryptAESState
387d7a
 {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    PK11Context *context;
387d7a
+#else
387d7a
     unsigned int w[44];
387d7a
     unsigned char state[16];
387d7a
+#endif
387d7a
     unsigned char cbc[16];
387d7a
     unsigned char buf[16];
387d7a
     bool paddingReached; // encryption only
387d7a
@@ -87,8 +99,12 @@ struct DecryptAESState {
387d7a
 
387d7a
 struct DecryptAES256State
387d7a
 {
387d7a
+#ifdef ENABLE_NSS3
387d7a
+    PK11Context *context;
387d7a
+#else
387d7a
     unsigned int w[60];
387d7a
     unsigned char state[16];
387d7a
+#endif
387d7a
     unsigned char cbc[16];
387d7a
     unsigned char buf[16];
387d7a
     bool paddingReached; // encryption only
387d7a
diff --git a/poppler/poppler-config.h.cmake b/poppler/poppler-config.h.cmake
387d7a
index f0a5a1a0..dcaade6f 100644
387d7a
--- a/poppler/poppler-config.h.cmake
387d7a
+++ b/poppler/poppler-config.h.cmake
387d7a
@@ -115,6 +115,12 @@
387d7a
 #cmakedefine HAVE_SPLASH 1
387d7a
 #endif
387d7a
 
387d7a
+/* Build against libnss3 for digital signature validation and
387d7a
+   implementation of encryption/decryption. */
387d7a
+#ifndef ENABLE_NSS3
387d7a
+#cmakedefine ENABLE_NSS3 1
387d7a
+#endif
387d7a
+
387d7a
 //------------------------------------------------------------------------
387d7a
 // version
387d7a
 //------------------------------------------------------------------------