diff --git a/SOURCES/poppler-0.66.0-JPXStream-length.patch b/SOURCES/poppler-0.66.0-JPXStream-length.patch new file mode 100644 index 0000000..ecd52b8 --- /dev/null +++ b/SOURCES/poppler-0.66.0-JPXStream-length.patch @@ -0,0 +1,26 @@ +From 68ef84e5968a4249c2162b839ca6d7975048a557 Mon Sep 17 00:00:00 2001 +From: Albert Astals Cid +Date: Mon, 15 Jul 2019 23:24:22 +0200 +Subject: [PATCH] JPXStream::init: ignore dict Length if clearly broken + +Fixes issue #805 +--- + poppler/JPEG2000Stream.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/poppler/JPEG2000Stream.cc b/poppler/JPEG2000Stream.cc +index 0eea3a2d..8e6902f4 100644 +--- a/poppler/JPEG2000Stream.cc ++++ b/poppler/JPEG2000Stream.cc +@@ -219,7 +219,7 @@ void JPXStream::init() + } + + int bufSize = BUFFER_INITIAL_SIZE; +- if (oLen.isInt()) bufSize = oLen.getInt(); ++ if (oLen.isInt() && oLen.getInt() > 0) bufSize = oLen.getInt(); + + GBool indexed = gFalse; + if (cspace.isArray() && cspace.arrayGetLength() > 0) { +-- +2.21.0 + diff --git a/SOURCES/poppler-0.66.0-PSOutputDev-rgb.patch b/SOURCES/poppler-0.66.0-PSOutputDev-rgb.patch new file mode 100644 index 0000000..70f173a --- /dev/null +++ b/SOURCES/poppler-0.66.0-PSOutputDev-rgb.patch @@ -0,0 +1,372 @@ +From 64aa150a92ccb082db6a3383fa734a6ac91cf1bf Mon Sep 17 00:00:00 2001 +From: Marek Kasik +Date: Tue, 30 Apr 2019 18:47:44 +0200 +Subject: [PATCH] PSOutputDev: Don't read outside of image buffer + +Check whether input image is RGB or BGR to not treat +it as CMYK in those cases in PSOutputDev::checkPageSlice(). + +Fixes #751 +--- + poppler/PSOutputDev.cc | 248 ++++++++++++++++++++++++++++++++--------- + 1 file changed, 196 insertions(+), 52 deletions(-) + +diff --git a/poppler/PSOutputDev.cc b/poppler/PSOutputDev.cc +index 0d201835..155a8cbe 100644 +--- a/poppler/PSOutputDev.cc ++++ b/poppler/PSOutputDev.cc +@@ -3385,13 +3385,21 @@ GBool PSOutputDev::checkPageSlice(Page * + } + break; + case psLevel1Sep: ++ GfxColor inputColor; ++ GfxCMYK cmyk; ++ unsigned char cmykColor[4]; ++ GfxDeviceRGBColorSpace *rgbCS; ++ SplashColorMode colorMode; ++ ++ colorMode = bitmap->getMode(); ++ + p = bitmap->getDataPtr(); + // Check for an all gray image + if (getOptimizeColorSpace()) { + isGray = gTrue; + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { +- if (p[4*x] != p[4*x + 1] || p[4*x] != p[4*x + 2]) { ++ if (p[numComps*x] != p[numComps*x + 1] || p[numComps*x] != p[numComps*x + 2]) { + isGray = gFalse; + y = h; + break; +@@ -3411,7 +3419,9 @@ GBool PSOutputDev::checkPageSlice(Page * + col[0] = col[1] = col[2] = col[3] = 0; + if (isGray) { + int g; +- if ((psProcessBlack & processColors) == 0) { ++ if ((psProcessBlack & processColors) == 0 && ++ colorMode != splashModeRGB8 && ++ colorMode != splashModeBGR8) { + // Check if the image uses black + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { +@@ -3425,59 +3435,23 @@ GBool PSOutputDev::checkPageSlice(Page * + } + p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); + } +- for (y = 0; y < h; ++y) { +- if (useBinary) { +- // Binary gray image +- for (x = 0; x < w; ++x) { +- g = p[4*x] + p[4*x + 3]; +- g = 255 - g; +- if (g < 0) g = 0; +- hexBuf[i++] = (Guchar) g; +- if (i >= 64) { +- writePSBuf(hexBuf, i); +- i = 0; +- } +- } +- } else { +- // Hex gray image +- for (x = 0; x < w; ++x) { +- g = p[4*x] + p[4*x + 3]; +- g = 255 - g; +- if (g < 0) g = 0; +- digit = g / 16; +- hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); +- digit = g % 16; +- hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); +- if (i >= 64) { +- hexBuf[i++] = '\n'; +- writePSBuf(hexBuf, i); +- i = 0; +- } +- } +- } +- p -= bitmap->getRowSize(); +- } +- } else if (((psProcessCyan | psProcessMagenta | psProcessYellow | psProcessBlack) & ~processColors) != 0) { +- // Color image, need to check color flags for each dot +- for (y = 0; y < h; ++y) { +- for (comp = 0; comp < 4; ++comp) { ++ if (colorMode == splashModeRGB8 || colorMode != splashModeBGR8) { ++ for (y = 0; y < h; ++y) { + if (useBinary) { +- // Binary color image ++ // Binary gray image + for (x = 0; x < w; ++x) { +- col[comp] |= p[4*x + comp]; +- hexBuf[i++] = p[4*x + comp]; ++ hexBuf[i++] = (Guchar) p[3*x]; + if (i >= 64) { + writePSBuf(hexBuf, i); + i = 0; + } + } + } else { +- // Gray color image ++ // Hex gray image + for (x = 0; x < w; ++x) { +- col[comp] |= p[4*x + comp]; +- digit = p[4*x + comp] / 16; ++ digit = p[3*x] / 16; + hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); +- digit = p[4*x + comp] % 16; ++ digit = p[3*x] % 16; + hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); + if (i >= 64) { + hexBuf[i++] = '\n'; +@@ -3485,29 +3459,31 @@ GBool PSOutputDev::checkPageSlice(Page * + i = 0; + } + } +- } ++ } + } +- p -= bitmap->getRowSize(); +- } +- } else { +- // Color image, do not need to check color flags +- for (y = 0; y < h; ++y) { +- for (comp = 0; comp < 4; ++comp) { ++ } else { ++ for (y = 0; y < h; ++y) { + if (useBinary) { +- // Binary color image ++ // Binary gray image + for (x = 0; x < w; ++x) { +- hexBuf[i++] = p[4*x + comp]; ++ g = p[4*x] + p[4*x + 3]; ++ g = 255 - g; ++ if (g < 0) g = 0; ++ hexBuf[i++] = (Guchar) g; + if (i >= 64) { + writePSBuf(hexBuf, i); + i = 0; + } + } + } else { +- // Hex color image ++ // Hex gray image + for (x = 0; x < w; ++x) { +- digit = p[4*x + comp] / 16; ++ g = p[4*x] + p[4*x + 3]; ++ g = 255 - g; ++ if (g < 0) g = 0; ++ digit = g / 16; + hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); +- digit = p[4*x + comp] % 16; ++ digit = g % 16; + hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); + if (i >= 64) { + hexBuf[i++] = '\n'; +@@ -3515,9 +3491,207 @@ GBool PSOutputDev::checkPageSlice(Page * + i = 0; + } + } +- } ++ } + } +- p -= bitmap->getRowSize(); ++ } ++ p -= bitmap->getRowSize(); ++ } else if (((psProcessCyan | psProcessMagenta | psProcessYellow | psProcessBlack) & ~processColors) != 0) { ++ // Color image, need to check color flags for each dot ++ switch (colorMode) { ++ case splashModeRGB8: ++ case splashModeBGR8: ++ rgbCS = new GfxDeviceRGBColorSpace(); ++ for (y = 0; y < h; ++y) { ++ for (comp = 0; comp < 4; ++comp) { ++ if (useBinary) { ++ // Binary color image ++ for (x = 0; x < w; ++x) { ++ if (likely(colorMode == splashModeRGB8)) { ++ inputColor.c[0] = byteToCol(p[3*x + 0]); ++ inputColor.c[1] = byteToCol(p[3*x + 1]); ++ inputColor.c[2] = byteToCol(p[3*x + 2]); ++ } else { ++ inputColor.c[0] = byteToCol(p[3*x + 2]); ++ inputColor.c[1] = byteToCol(p[3*x + 1]); ++ inputColor.c[2] = byteToCol(p[3*x + 0]); ++ } ++ rgbCS->getCMYK(&inputColor, &cmyk); ++ cmykColor[0] = colToByte(cmyk.c); ++ cmykColor[1] = colToByte(cmyk.m); ++ cmykColor[2] = colToByte(cmyk.y); ++ cmykColor[3] = colToByte(cmyk.k); ++ ++ col[comp] |= cmykColor[comp]; ++ hexBuf[i++] = cmykColor[comp]; ++ if (i >= 64) { ++ writePSBuf(hexBuf, i); ++ i = 0; ++ } ++ } ++ } else { ++ // Gray color image ++ for (x = 0; x < w; ++x) { ++ if (likely(colorMode == splashModeRGB8)) { ++ inputColor.c[0] = byteToCol(p[3*x + 0]); ++ inputColor.c[1] = byteToCol(p[3*x + 1]); ++ inputColor.c[2] = byteToCol(p[3*x + 2]); ++ } else { ++ inputColor.c[0] = byteToCol(p[3*x + 2]); ++ inputColor.c[1] = byteToCol(p[3*x + 1]); ++ inputColor.c[2] = byteToCol(p[3*x + 0]); ++ } ++ rgbCS->getCMYK(&inputColor, &cmyk); ++ cmykColor[0] = colToByte(cmyk.c); ++ cmykColor[1] = colToByte(cmyk.m); ++ cmykColor[2] = colToByte(cmyk.y); ++ cmykColor[3] = colToByte(cmyk.k); ++ ++ col[comp] |= cmykColor[comp]; ++ digit = cmykColor[comp] / 16; ++ hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); ++ digit = cmykColor[comp] % 16; ++ hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); ++ if (i >= 64) { ++ hexBuf[i++] = '\n'; ++ writePSBuf(hexBuf, i); ++ i = 0; ++ } ++ } ++ } ++ } ++ p -= bitmap->getRowSize(); ++ } ++ delete rgbCS; ++ break; ++ default: ++ for (y = 0; y < h; ++y) { ++ for (comp = 0; comp < 4; ++comp) { ++ if (useBinary) { ++ // Binary color image ++ for (x = 0; x < w; ++x) { ++ col[comp] |= p[4*x + comp]; ++ hexBuf[i++] = p[4*x + comp]; ++ if (i >= 64) { ++ writePSBuf(hexBuf, i); ++ i = 0; ++ } ++ } ++ } else { ++ // Gray color image ++ for (x = 0; x < w; ++x) { ++ col[comp] |= p[4*x + comp]; ++ digit = p[4*x + comp] / 16; ++ hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); ++ digit = p[4*x + comp] % 16; ++ hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); ++ if (i >= 64) { ++ hexBuf[i++] = '\n'; ++ writePSBuf(hexBuf, i); ++ i = 0; ++ } ++ } ++ } ++ } ++ p -= bitmap->getRowSize(); ++ } ++ break; ++ } ++ } else { ++ // Color image, do not need to check color flags ++ switch (colorMode) { ++ case splashModeRGB8: ++ case splashModeBGR8: ++ rgbCS = new GfxDeviceRGBColorSpace(); ++ for (y = 0; y < h; ++y) { ++ for (comp = 0; comp < 4; ++comp) { ++ if (useBinary) { ++ // Binary color image ++ for (x = 0; x < w; ++x) { ++ if (likely(colorMode == splashModeRGB8)) { ++ inputColor.c[0] = byteToCol(p[3*x + 0]); ++ inputColor.c[1] = byteToCol(p[3*x + 1]); ++ inputColor.c[2] = byteToCol(p[3*x + 2]); ++ } else { ++ inputColor.c[0] = byteToCol(p[3*x + 2]); ++ inputColor.c[1] = byteToCol(p[3*x + 1]); ++ inputColor.c[2] = byteToCol(p[3*x + 0]); ++ } ++ rgbCS->getCMYK(&inputColor, &cmyk); ++ cmykColor[0] = colToByte(cmyk.c); ++ cmykColor[1] = colToByte(cmyk.m); ++ cmykColor[2] = colToByte(cmyk.y); ++ cmykColor[3] = colToByte(cmyk.k); ++ ++ hexBuf[i++] = cmykColor[comp]; ++ if (i >= 64) { ++ writePSBuf(hexBuf, i); ++ i = 0; ++ } ++ } ++ } else { ++ // Hex color image ++ for (x = 0; x < w; ++x) { ++ if (likely(colorMode == splashModeRGB8)) { ++ inputColor.c[0] = byteToCol(p[3*x + 0]); ++ inputColor.c[1] = byteToCol(p[3*x + 1]); ++ inputColor.c[2] = byteToCol(p[3*x + 2]); ++ } else { ++ inputColor.c[0] = byteToCol(p[3*x + 2]); ++ inputColor.c[1] = byteToCol(p[3*x + 1]); ++ inputColor.c[2] = byteToCol(p[3*x + 0]); ++ } ++ rgbCS->getCMYK(&inputColor, &cmyk); ++ cmykColor[0] = colToByte(cmyk.c); ++ cmykColor[1] = colToByte(cmyk.m); ++ cmykColor[2] = colToByte(cmyk.y); ++ cmykColor[3] = colToByte(cmyk.k); ++ ++ digit = cmykColor[comp] / 16; ++ hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); ++ digit = cmykColor[comp] % 16; ++ hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); ++ if (i >= 64) { ++ hexBuf[i++] = '\n'; ++ writePSBuf(hexBuf, i); ++ i = 0; ++ } ++ } ++ } ++ } ++ p -= bitmap->getRowSize(); ++ } ++ delete rgbCS; ++ break; ++ default: ++ for (y = 0; y < h; ++y) { ++ for (comp = 0; comp < 4; ++comp) { ++ if (useBinary) { ++ // Binary color image ++ for (x = 0; x < w; ++x) { ++ hexBuf[i++] = p[4*x + comp]; ++ if (i >= 64) { ++ writePSBuf(hexBuf, i); ++ i = 0; ++ } ++ } ++ } else { ++ // Hex color image ++ for (x = 0; x < w; ++x) { ++ digit = p[4*x + comp] / 16; ++ hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); ++ digit = p[4*x + comp] % 16; ++ hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); ++ if (i >= 64) { ++ hexBuf[i++] = '\n'; ++ writePSBuf(hexBuf, i); ++ i = 0; ++ } ++ } ++ } ++ } ++ p -= bitmap->getRowSize(); ++ } ++ break; + } + } + if (i != 0) { diff --git a/SOURCES/poppler-0.66.0-jpeg2000-component-size.patch b/SOURCES/poppler-0.66.0-jpeg2000-component-size.patch new file mode 100644 index 0000000..2ad86a8 --- /dev/null +++ b/SOURCES/poppler-0.66.0-jpeg2000-component-size.patch @@ -0,0 +1,41 @@ +From 89a5367d49b2556a2635dbb6d48d6a6b182a2c6c Mon Sep 17 00:00:00 2001 +From: Albert Astals Cid +Date: Thu, 23 May 2019 00:54:29 +0200 +Subject: [PATCH] JPEG2000Stream: fail gracefully if not all components have + the same WxH + +I think this is just a mistake, or at least the only file we have with +this scenario is a fuzzed one +--- + poppler/JPEG2000Stream.cc | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/poppler/JPEG2000Stream.cc b/poppler/JPEG2000Stream.cc +index 15bbcae4..0eea3a2d 100644 +--- a/poppler/JPEG2000Stream.cc ++++ b/poppler/JPEG2000Stream.cc +@@ -4,7 +4,7 @@ + // + // A JPX stream decoder using OpenJPEG + // +-// Copyright 2008-2010, 2012, 2017, 2018 Albert Astals Cid ++// Copyright 2008-2010, 2012, 2017-2019 Albert Astals Cid + // Copyright 2011 Daniel Glöckner + // Copyright 2014, 2016 Thomas Freitag + // Copyright 2013, 2014 Adrian Johnson +@@ -253,6 +253,12 @@ void JPXStream::init() + close(); + break; + } ++ const int componentPixels = priv->image->comps[component].w * priv->image->comps[component].h; ++ if (componentPixels != priv->npixels) { ++ error(errSyntaxWarning, -1, "Component {0:d} has different WxH than component 0", component); ++ close(); ++ break; ++ } + unsigned char *cdata = (unsigned char *)priv->image->comps[component].data; + int adjust = 0; + int depth = priv->image->comps[component].prec; +-- +2.21.0 + diff --git a/SOURCES/poppler-0.66.0-nss.patch b/SOURCES/poppler-0.66.0-nss.patch new file mode 100644 index 0000000..001e528 --- /dev/null +++ b/SOURCES/poppler-0.66.0-nss.patch @@ -0,0 +1,1072 @@ +diff --git a/config.h.cmake b/config.h.cmake +index 7989cbfb..6f5e147e 100644 +--- a/config.h.cmake ++++ b/config.h.cmake +@@ -24,9 +24,6 @@ + /* Use zlib instead of builtin zlib decoder to uncompress flate streams. */ + #cmakedefine ENABLE_ZLIB_UNCOMPRESS 1 + +-/* Build against libnss3 for digital signature validation */ +-#cmakedefine ENABLE_NSS3 1 +- + /* Use cairo for rendering. */ + #cmakedefine HAVE_CAIRO 1 + +diff --git a/poppler/Decrypt.cc b/poppler/Decrypt.cc +index 16476f4f..9f4adda3 100644 +--- a/poppler/Decrypt.cc ++++ b/poppler/Decrypt.cc +@@ -39,17 +39,33 @@ + #include "goo/grandom.h" + #include "Decrypt.h" + #include "Error.h" +- ++#ifdef ENABLE_NSS3 ++#include ++#include ++#include ++#endif ++ ++#ifdef ENABLE_NSS3 ++static PK11Context *rc4InitContext(const Guchar *key, int keyLen); ++static Guchar rc4DecryptByte(PK11Context *context, Guchar c); ++#else + static void rc4InitKey(Guchar *key, int keyLen, Guchar *state); + static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c); ++#endif + + static GBool aesReadBlock(Stream *str, Guchar *in, GBool addPadding); + ++#ifdef ENABLE_NSS3 ++static PK11Context *aesInitContext(Guchar *in, Guchar *objKey, int objKeyLength, GBool decrypt); ++#else + static void aesKeyExpansion(DecryptAESState *s, Guchar *objKey, int objKeyLen, GBool decrypt); ++#endif + static void aesEncryptBlock(DecryptAESState *s, Guchar *in); + static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last); + ++#ifndef ENABLE_NSS3 + static void aes256KeyExpansion(DecryptAES256State *s, Guchar *objKey, int objKeyLen, GBool decrypt); ++#endif + static void aes256EncryptBlock(DecryptAES256State *s, Guchar *in); + static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last); + +@@ -70,6 +86,31 @@ static const Guchar passwordPad[32] = { + // Decrypt + //------------------------------------------------------------------------ + ++#ifdef ENABLE_NSS3 ++static void shutdownNSS() ++{ ++ if (NSS_Shutdown() != SECSuccess) { ++ error(errInternal, -1, "NSS shutdown failed: {0:s}", ++ PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); ++ } ++} ++ ++static GBool initNSS() { ++ if (NSS_IsInitialized()) { ++ return gTrue; ++ } else { ++ if (NSS_NoDB_Init(".") != SECSuccess) { ++ error(errInternal, -1, "NSS initialization failed: {0:s}", ++ PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); ++ return gFalse; ++ } else { ++ atexit(shutdownNSS); ++ return gTrue; ++ } ++ } ++} ++#endif ++ + GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength, + GooString *ownerKey, GooString *userKey, + GooString *ownerEnc, GooString *userEnc, +@@ -80,13 +121,21 @@ GBool Decrypt::makeFileKey(int encVersio + DecryptAES256State state; + Guchar test[127 + 56], test2[32]; + GooString *userPassword2; +- Guchar fState[256]; + Guchar tmpKey[16]; +- Guchar fx, fy; + int len, i, j; ++#ifdef ENABLE_NSS3 ++ PK11Context *rc4Context; ++#else ++ Guchar fState[256]; ++ Guchar fx, fy; ++#endif + + *ownerPasswordOk = gFalse; + ++#ifdef ENABLE_NSS3 ++ initNSS(); ++#endif ++ + if (encRevision == 5 || encRevision == 6) { + + // check the owner password +@@ -115,15 +164,27 @@ GBool Decrypt::makeFileKey(int encVersio + //test contains the initial SHA-256 hash input K. + revision6Hash(ownerPassword, test, userKey->getCString()); + } ++#ifndef ENABLE_NSS3 + aes256KeyExpansion(&state, test, 32, gTrue); ++#endif + for (i = 0; i < 16; ++i) { + state.cbc[i] = 0; + } ++#ifdef ENABLE_NSS3 ++ state.context = aesInitContext(state.cbc, test, 32, gTrue); ++ if (state.context) { ++#endif + aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString(), gFalse); + memcpy(fileKey, state.buf, 16); + aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString() + 16, + gFalse); + memcpy(fileKey + 16, state.buf, 16); ++#ifdef ENABLE_NSS3 ++ PK11_DestroyContext(state.context, PR_TRUE); ++ } else { ++ return gFalse; ++ } ++#endif + + *ownerPasswordOk = gTrue; + return gTrue; +@@ -156,15 +217,27 @@ GBool Decrypt::makeFileKey(int encVersio + //user key is not used in computing intermediate user key. + revision6Hash(userPassword, test, nullptr); + } ++#ifndef ENABLE_NSS3 + aes256KeyExpansion(&state, test, 32, gTrue); ++#endif + for (i = 0; i < 16; ++i) { + state.cbc[i] = 0; + } ++#ifdef ENABLE_NSS3 ++ state.context = aesInitContext(state.cbc, test, 32, gTrue); ++ if (state.context) { ++#endif + aes256DecryptBlock(&state, (Guchar *)userEnc->getCString(), gFalse); + memcpy(fileKey, state.buf, 16); + aes256DecryptBlock(&state, (Guchar *)userEnc->getCString() + 16, + gFalse); + memcpy(fileKey + 16, state.buf, 16); ++#ifdef ENABLE_NSS3 ++ PK11_DestroyContext(state.context, PR_TRUE); ++ } else { ++ return gFalse; ++ } ++#endif + + return gTrue; + } +@@ -189,22 +262,41 @@ GBool Decrypt::makeFileKey(int encVersio + } + } + if (encRevision == 2) { ++#ifdef ENABLE_NSS3 ++ rc4Context = rc4InitContext(test, keyLength); ++ if (rc4Context) { ++ for (i = 0; i < 32; ++i) ++ test2[i] = rc4DecryptByte(rc4Context, ownerKey->getChar(i)); ++ PK11_DestroyContext(rc4Context, PR_TRUE); ++ } ++#else + rc4InitKey(test, keyLength, fState); + fx = fy = 0; + for (i = 0; i < 32; ++i) { + test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i)); + } ++#endif + } else { + memcpy(test2, ownerKey->getCString(), 32); + for (i = 19; i >= 0; --i) { + for (j = 0; j < keyLength; ++j) { + tmpKey[j] = test[j] ^ i; + } ++#ifdef ENABLE_NSS3 ++ rc4Context = rc4InitContext(tmpKey, keyLength); ++ if (rc4Context) { ++ for (j = 0; j < 32; ++j) { ++ test2[j] = rc4DecryptByte(rc4Context, test2[j]); ++ } ++ PK11_DestroyContext(rc4Context, PR_TRUE); ++ } ++#else + rc4InitKey(tmpKey, keyLength, fState); + fx = fy = 0; + for (j = 0; j < 32; ++j) { + test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]); + } ++#endif + } + } + userPassword2 = new GooString((char *)test2, 32); +@@ -232,11 +324,15 @@ GBool Decrypt::makeFileKey2(int encVersi + GBool encryptMetadata) { + Guchar *buf; + Guchar test[32]; +- Guchar fState[256]; + Guchar tmpKey[16]; +- Guchar fx, fy; + int len, i, j; +- GBool ok; ++ GBool ok = gTrue; ++#ifdef ENABLE_NSS3 ++ PK11Context *rc4Context; ++#else ++ Guchar fState[256]; ++ Guchar fx, fy; ++#endif + + // generate file key + buf = (Guchar *)gmalloc(72 + fileID->getLength()); +@@ -273,28 +369,52 @@ GBool Decrypt::makeFileKey2(int encVersi + + // test user password + if (encRevision == 2) { ++#ifdef ENABLE_NSS3 ++ rc4Context = rc4InitContext(fileKey, keyLength); ++ if (rc4Context) { ++ for (i = 0; i < 32; ++i) ++ test[i] = rc4DecryptByte(rc4Context, userKey->getChar(i)); ++ PK11_DestroyContext(rc4Context, PR_TRUE); ++ } else { ++ ok = gFalse; ++ } ++#else + rc4InitKey(fileKey, keyLength, fState); + fx = fy = 0; + for (i = 0; i < 32; ++i) { + test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i)); + } +- ok = memcmp(test, passwordPad, 32) == 0; ++#endif ++ if (ok) ++ ok = memcmp(test, passwordPad, 32) == 0; + } else if (encRevision == 3) { + memcpy(test, userKey->getCString(), 32); + for (i = 19; i >= 0; --i) { + for (j = 0; j < keyLength; ++j) { + tmpKey[j] = fileKey[j] ^ i; + } ++#ifdef ENABLE_NSS3 ++ rc4Context = rc4InitContext(tmpKey, keyLength); ++ if (rc4Context) { ++ for (j = 0; j < 32; ++j) ++ test[j] = rc4DecryptByte(rc4Context, test[j]); ++ PK11_DestroyContext(rc4Context, PR_TRUE); ++ } else { ++ ok = gFalse; ++ } ++#else + rc4InitKey(tmpKey, keyLength, fState); + fx = fy = 0; + for (j = 0; j < 32; ++j) { + test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]); + } ++#endif + } + memcpy(buf, passwordPad, 32); + memcpy(buf + 32, fileID->getCString(), fileID->getLength()); + md5(buf, 32 + fileID->getLength(), buf); +- ok = memcmp(test, buf, 16) == 0; ++ if (ok) ++ ok = memcmp(test, buf, 16) == 0; + } else { + ok = gFalse; + } +@@ -334,6 +454,9 @@ BaseCryptStream::BaseCryptStream(Stream + if ((objKeyLength = keyLength + 5) > 16) { + objKeyLength = 16; + } ++#ifdef ENABLE_NSS3 ++ state.rc4.context = nullptr; ++#endif + break; + case cryptAES: + objKey[keyLength] = objNum & 0xff; +@@ -349,9 +472,15 @@ BaseCryptStream::BaseCryptStream(Stream + if ((objKeyLength = keyLength + 5) > 16) { + objKeyLength = 16; + } ++#ifdef ENABLE_NSS3 ++ state.aes.context = nullptr; ++#endif + break; + case cryptAES256: + objKeyLength = keyLength; ++#ifdef ENABLE_NSS3 ++ state.aes256.context = nullptr; ++#endif + break; + case cryptNone: + break; +@@ -359,9 +488,32 @@ BaseCryptStream::BaseCryptStream(Stream + + charactersRead = 0; + autoDelete = gTrue; ++ ++#ifdef ENABLE_NSS3 ++ initNSS(); ++#endif + } + + BaseCryptStream::~BaseCryptStream() { ++#ifdef ENABLE_NSS3 ++ switch (algo) { ++ case cryptRC4: ++ if (state.rc4.context) ++ PK11_DestroyContext(state.rc4.context, PR_TRUE); ++ break; ++ case cryptAES: ++ if (state.aes.context) ++ PK11_DestroyContext(state.aes.context, PR_TRUE); ++ break; ++ case cryptAES256: ++ if (state.aes256.context) ++ PK11_DestroyContext(state.aes256.context, PR_TRUE); ++ break; ++ default: ++ break; ++ } ++#endif ++ + if (autoDelete) { + delete str; + } +@@ -424,18 +576,40 @@ void EncryptStream::reset() { + + switch (algo) { + case cryptRC4: ++#ifdef ENABLE_NSS3 ++ if (state.rc4.context) ++ PK11_DestroyContext(state.rc4.context, PR_TRUE); ++ state.rc4.context = rc4InitContext(objKey, objKeyLength); ++#else + state.rc4.x = state.rc4.y = 0; + rc4InitKey(objKey, objKeyLength, state.rc4.state); ++#endif + break; + case cryptAES: ++#ifdef ENABLE_NSS3 ++ memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf ++ if (state.aes.context) ++ PK11_DestroyContext(state.aes.context, PR_TRUE); ++ state.aes.context = aesInitContext(state.aes.cbc, objKey, objKeyLength, ++ gFalse); ++#else + aesKeyExpansion(&state.aes, objKey, objKeyLength, gFalse); + memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf ++#endif + state.aes.bufIdx = 0; + state.aes.paddingReached = gFalse; + break; + case cryptAES256: ++#ifdef ENABLE_NSS3 ++ memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf ++ if (state.aes256.context) ++ PK11_DestroyContext(state.aes256.context, PR_TRUE); ++ state.aes256.context = aesInitContext(state.aes256.cbc, objKey, objKeyLength, ++ gFalse); ++#else + aes256KeyExpansion(&state.aes256, objKey, objKeyLength, gFalse); + memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf ++#endif + state.aes256.bufIdx = 0; + state.aes256.paddingReached = gFalse; + break; +@@ -456,7 +630,11 @@ int EncryptStream::lookChar() { + case cryptRC4: + if ((c = str->getChar()) != EOF) { + // RC4 is XOR-based: the decryption algorithm works for encryption too ++#ifdef ENABLE_NSS3 ++ c = rc4DecryptByte(state.rc4.context, (Guchar)c); ++#else + c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (Guchar)c); ++#endif + } + break; + case cryptAES: +@@ -506,21 +684,47 @@ void DecryptStream::reset() { + + switch (algo) { + case cryptRC4: ++#ifdef ENABLE_NSS3 ++ if (state.rc4.context) ++ PK11_DestroyContext(state.rc4.context, PR_TRUE); ++ state.rc4.context = rc4InitContext(objKey, objKeyLength); ++#else + state.rc4.x = state.rc4.y = 0; + rc4InitKey(objKey, objKeyLength, state.rc4.state); ++#endif + break; + case cryptAES: ++#ifdef ENABLE_NSS3 ++ if (state.aes.context) ++ PK11_DestroyContext(state.aes.context, PR_TRUE); ++ for (i = 0; i < 16; ++i) { ++ state.aes.cbc[i] = str->getChar(); ++ } ++ state.aes.context = aesInitContext(state.aes.cbc, objKey, objKeyLength, ++ gTrue); ++#else + aesKeyExpansion(&state.aes, objKey, objKeyLength, gTrue); + for (i = 0; i < 16; ++i) { + state.aes.cbc[i] = str->getChar(); + } ++#endif + state.aes.bufIdx = 16; + break; + case cryptAES256: ++#ifdef ENABLE_NSS3 ++ if (state.aes256.context) ++ PK11_DestroyContext(state.aes256.context, PR_TRUE); ++ for (i = 0; i < 16; ++i) { ++ state.aes256.cbc[i] = str->getChar(); ++ } ++ state.aes256.context = aesInitContext(state.aes256.cbc, objKey, objKeyLength, ++ gTrue); ++#else + aes256KeyExpansion(&state.aes256, objKey, objKeyLength, gTrue); + for (i = 0; i < 16; ++i) { + state.aes256.cbc[i] = str->getChar(); + } ++#endif + state.aes256.bufIdx = 16; + break; + case cryptNone: +@@ -539,10 +743,21 @@ int DecryptStream::lookChar() { + switch (algo) { + case cryptRC4: + if ((c = str->getChar()) != EOF) { ++#ifdef ENABLE_NSS3 ++ if (unlikely(state.rc4.context == nullptr)) ++ c = EOF; ++ else ++ c = rc4DecryptByte(state.rc4.context, (Guchar)c); ++#else + c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (Guchar)c); ++#endif + } + break; + case cryptAES: ++#ifdef ENABLE_NSS3 ++ if (unlikely(state.aes.context == nullptr)) ++ break; ++#endif + if (state.aes.bufIdx == 16) { + if (aesReadBlock(str, in, gFalse)) { + aesDecryptBlock(&state.aes, in, str->lookChar() == EOF); +@@ -555,6 +770,10 @@ int DecryptStream::lookChar() { + } + break; + case cryptAES256: ++#ifdef ENABLE_NSS3 ++ if (unlikely(state.aes256.context == nullptr)) ++ break; ++#endif + if (state.aes256.bufIdx == 16) { + if (aesReadBlock(str, in, gFalse)) { + aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF); +@@ -576,6 +795,175 @@ int DecryptStream::lookChar() { + // RC4-compatible decryption + //------------------------------------------------------------------------ + ++#ifdef ENABLE_NSS3 ++/* ++ * This function turns given key into token key (compared to a session key ++ * which is prohibited in FIPS mode). ++ */ ++static PK11SymKey *tokenizeKey(const Guchar *key, int keyLen, ++ CK_ATTRIBUTE_TYPE operation) { ++ CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC_PAD; ++ PK11SlotInfo *slot; ++ PK11SymKey *wrappingKey = nullptr; ++ PK11SymKey *symKey = nullptr; ++ PK11SymKey *token = nullptr; ++ SECStatus retval; ++ SECItem *secParam = nullptr; ++ SECItem ivItem, wrappedKey; ++ Guchar output[48]; // Buffer to hold 256 bit key + padding ++ Guchar iv[16]; // Initialization vector for AES ++ Guint outputLength; ++ int i; ++ ++ slot = PK11_GetBestSlot(CKM_AES_KEY_GEN, nullptr); ++ if (slot == nullptr) { ++ error(errInternal, -1, "Unable to find security device (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ // Generate random key for wrapping of given key by AES-256 ++ wrappingKey = PK11_KeyGen(slot, CKM_AES_KEY_GEN, nullptr, 32, nullptr); ++ if (wrappingKey == nullptr) { ++ error(errInternal, -1, "Failed to generate wrapping key (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ for (i = 0; i < 16; i++) ++ iv[i] = i; ++ ++ ivItem.type = siBuffer; ++ ivItem.data = iv; ++ ivItem.len = 16; ++ ++ secParam = PK11_ParamFromIV(cipherMech, &ivItem); ++ if (secParam == nullptr) { ++ error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ // Encrypt given key ++ retval = PK11_Encrypt(wrappingKey, ++ cipherMech, ++ secParam, ++ output, ++ &outputLength, ++ sizeof(output), ++ key, ++ keyLen); ++ if (retval != SECSuccess) { ++ error(errInternal, -1, "Failed to encrypt key (err {0:d})", ++ PR_GetError()); ++ } ++ ++ wrappedKey.type = siBuffer; ++ wrappedKey.data = output; ++ wrappedKey.len = outputLength; ++ ++ // Unwrap the wrapped key to token so it can be used in FIPS mode ++ token = PK11_UnwrapSymKey(wrappingKey, ++ cipherMech, ++ &ivItem, ++ &wrappedKey, ++ operation, ++ CKA_UNWRAP, ++ keyLen); ++ ++ if (token == nullptr) { ++ error(errInternal, -1, "Failed to unwrap symmetric key (err {0:d})", ++ PR_GetError()); ++ } ++ ++err: ++ if (secParam != nullptr) ++ SECITEM_FreeItem(secParam, PR_TRUE); ++ ++ if (wrappingKey != nullptr) ++ PK11_FreeSymKey(wrappingKey); ++ ++ if (symKey != nullptr) ++ PK11_FreeSymKey(symKey); ++ ++ if (slot != nullptr) ++ PK11_FreeSlot(slot); ++ ++ return token; ++} ++ ++static PK11Context *rc4InitContext(const Guchar *key, int keyLen) { ++ CK_MECHANISM_TYPE cipherMech = CKM_RC4; ++ PK11SlotInfo *slot = nullptr; ++ PK11SymKey *symKey = nullptr; ++ SECItem *secParam = nullptr; ++ PK11Context *context = nullptr; ++ ++ slot = PK11_GetBestSlot(cipherMech, nullptr); ++ if (slot == nullptr) { ++ error(errInternal, -1, "Unable to find security device (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ symKey = tokenizeKey(key, keyLen, cipherMech); ++ if (symKey == nullptr) { ++ error(errInternal, -1, "Failed to create token from key (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ secParam = PK11_ParamFromIV(cipherMech, nullptr); ++ if (secParam == nullptr) { ++ error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ context = PK11_CreateContextBySymKey(cipherMech, ++ CKA_DECRYPT, ++ symKey, ++ secParam); ++ if (context == nullptr) { ++ error(errInternal, -1, "Failed to create context (err {0:d})", ++ PR_GetError()); ++ } ++ ++err: ++ if (secParam != nullptr) ++ SECITEM_FreeItem(secParam, PR_TRUE); ++ ++ if (symKey != nullptr) ++ PK11_FreeSymKey(symKey); ++ ++ if (slot != nullptr) ++ PK11_FreeSlot(slot); ++ ++ return context; ++} ++ ++static Guchar rc4DecryptByte(PK11Context *context, Guchar c) { ++ Guchar outputChar = 0; ++ SECStatus retval; ++ int outputLength; ++ ++ retval = PK11_CipherOp(context, ++ &outputChar, ++ &outputLength, ++ 1, ++ &c, ++ 1); ++ ++ if (retval != SECSuccess) { ++ error(errInternal, -1, "Failed to decrypt byte (err {0:d})", ++ PR_GetError()); ++ } ++ ++ return outputChar; ++} ++ ++#else ++ + static void rc4InitKey(Guchar *key, int keyLen, Guchar *state) { + Guchar index1, index2; + Guchar t; +@@ -609,6 +997,8 @@ static Guchar rc4DecryptByte(Guchar *sta + return c ^ state[(tx + ty) % 256]; + } + ++#endif ++ + //------------------------------------------------------------------------ + // AES decryption + //------------------------------------------------------------------------ +@@ -639,6 +1029,178 @@ static GBool aesReadBlock(Stream *str, G + } + } + ++#ifdef ENABLE_NSS3 ++ ++static PK11Context *aesInitContext(Guchar *in, Guchar *objKey, ++ int objKeyLength, GBool decrypt) { ++ CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC; ++ CK_ATTRIBUTE_TYPE operationType = decrypt ? CKA_DECRYPT : CKA_ENCRYPT; ++ PK11SlotInfo *slot; ++ PK11SymKey *symKey = nullptr; ++ SECItem *secParam = nullptr; ++ PK11Context *context = nullptr; ++ SECItem ivItem; ++ ++ slot = PK11_GetBestSlot(cipherMech, nullptr); ++ if (slot == nullptr) { ++ error(errInternal, -1, "Unable to find security device (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ symKey = tokenizeKey(objKey, objKeyLength, cipherMech); ++ if (symKey == nullptr) { ++ error(errInternal, -1, "Failed to create token from key (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ ivItem.type = siBuffer; ++ ivItem.data = in; ++ ivItem.len = 16; ++ ++ secParam = PK11_ParamFromIV(cipherMech, &ivItem); ++ if (secParam == nullptr) { ++ error(errInternal, -1, "Failed to set up PKCS11 param (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ context = PK11_CreateContextBySymKey(cipherMech, ++ operationType, ++ symKey, ++ secParam); ++ ++err: ++ if (secParam != nullptr) ++ SECITEM_FreeItem(secParam, PR_TRUE); ++ ++ if (symKey != nullptr) ++ PK11_FreeSymKey(symKey); ++ ++ if (slot != nullptr) ++ PK11_FreeSlot(slot); ++ ++ return context; ++} ++ ++static void aesEncryptBlock(DecryptAESState *s, Guchar *in) { ++ SECStatus rv; ++ int outputLength; ++ ++ rv = PK11_CipherOp(s->context, ++ s->buf, ++ &outputLength, ++ 16, ++ in, ++ 16); ++ ++ if (rv != SECSuccess) { ++ error(errInternal, -1, "Failed to encrypt input block (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ s->bufIdx = 0; ++ ++err: ++ return; ++} ++ ++static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) { ++ SECStatus rv1; ++ int outputLen; ++ int n, i; ++ ++ rv1 = PK11_CipherOp(s->context, ++ s->buf, ++ &outputLen, ++ 16, ++ in, ++ 16); ++ ++ if (rv1 != SECSuccess) { ++ error(errInternal, -1, "Failed to decrypt input block (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ 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; ++ } ++ ++err: ++ return; ++} ++ ++static void aes256EncryptBlock(DecryptAES256State *s, Guchar *in) { ++ SECStatus rv; ++ int outputLength; ++ ++ rv = PK11_CipherOp(s->context, ++ s->buf, ++ &outputLength, ++ 16, ++ in, ++ 16); ++ ++ if (rv != SECSuccess) { ++ error(errInternal, -1, "Failed to encrypt input block (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ s->bufIdx = 0; ++ ++err: ++ return; ++} ++ ++static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, ++ GBool last) { ++ SECStatus rv1; ++ int outputLen; ++ int n, i; ++ ++ rv1 = PK11_CipherOp(s->context, ++ s->buf, ++ &outputLen, ++ 16, ++ in, ++ 16); ++ ++ if (rv1 != SECSuccess) { ++ error(errInternal, -1, "Failed to decrypt input block (err {0:d})", ++ PR_GetError()); ++ goto err; ++ } ++ ++ 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; ++ } ++ ++err: ++ return; ++} ++ ++#else ++ + static const Guchar 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, +@@ -1121,10 +1683,33 @@ static void aes256DecryptBlock(DecryptAE + } + } + ++#endif ++ + //------------------------------------------------------------------------ + // MD5 message digest + //------------------------------------------------------------------------ + ++#ifdef ENABLE_NSS3 ++static void hashFunc(const Guchar *msg, int msgLen, Guchar *hash, ++ HASH_HashType type) { ++ HASHContext *context; ++ Guint hashLen = 0; ++ ++ if (!initNSS()) ++ return; ++ ++ context = HASH_Create(type); ++ if (context == nullptr) ++ return; ++ ++ HASH_Begin(context); ++ HASH_Update(context, msg, msgLen); ++ HASH_End(context, hash, &hashLen, HASH_ResultLen(type)); ++ HASH_Destroy(context); ++} ++ ++#else ++ + // this works around a bug in older Sun compilers + static inline Gulong rotateLeft(Gulong x, int r) { + x &= 0xffffffff; +@@ -1151,7 +1736,12 @@ static inline Gulong md5Round4(Gulong a, + return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s); + } + ++#endif ++ + void md5(Guchar *msg, int msgLen, Guchar *digest) { ++#ifdef ENABLE_NSS3 ++ hashFunc(msg, msgLen, digest, HASH_AlgMD5); ++#else + Gulong x[16] = {}; + Gulong a, b, c, d, aa, bb, cc, dd; + int n64; +@@ -1296,12 +1886,14 @@ void md5(Guchar *msg, int msgLen, Guchar + digest[13] = (Guchar)((d >>= 8) & 0xff); + digest[14] = (Guchar)((d >>= 8) & 0xff); + digest[15] = (Guchar)((d >>= 8) & 0xff); ++#endif + } + + //------------------------------------------------------------------------ + // SHA-256 hash + //------------------------------------------------------------------------ + ++#ifndef ENABLE_NSS3 + static Guint sha256K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, +@@ -1400,8 +1992,12 @@ static void sha256HashBlock(Guchar *blk, + H[6] += g; + H[7] += h; + } ++#endif + + static void sha256(Guchar *msg, int msgLen, Guchar *hash) { ++#ifdef ENABLE_NSS3 ++ hashFunc(msg, msgLen, hash, HASH_AlgSHA256); ++#else + Guchar blk[64]; + Guint H[8]; + int blkLen, i; +@@ -1453,7 +2049,10 @@ static void sha256(Guchar *msg, int msgL + hash[i*4 + 2] = (Guchar)(H[i] >> 8); + hash[i*4 + 3] = (Guchar)H[i]; + } ++#endif + } ++ ++#ifndef ENABLE_NSS3 + //------------------------------------------------------------------------ + // SHA-512 hash (see FIPS 180-4) + //------------------------------------------------------------------------ +@@ -1557,8 +2156,12 @@ static void sha512HashBlock(Guchar *blk, + H[6] += g; + H[7] += h; + } ++#endif + + static void sha512(Guchar *msg, int msgLen, Guchar *hash) { ++#ifdef ENABLE_NSS3 ++ hashFunc(msg, msgLen, hash, HASH_AlgSHA512); ++#else + Guchar blk[128]; + uint64_t H[8]; + int blkLen = 0, i; +@@ -1622,6 +2225,7 @@ static void sha512(Guchar *msg, int msgL + hash[i*8 + 6] = (Guchar)(H[i] >> 8); + hash[i*8 + 7] = (Guchar)H[i]; + } ++#endif + } + + //------------------------------------------------------------------------ +@@ -1631,6 +2235,9 @@ static void sha512(Guchar *msg, int msgL + //1.Initial hash value is different. + //2.A 384 bit message digest is obtained by truncating the final hash value. + static void sha384(Guchar *msg, int msgLen, Guchar *hash) { ++#ifdef ENABLE_NSS3 ++ hashFunc(msg, msgLen, hash, HASH_AlgSHA384); ++#else + Guchar blk[128]; + uint64_t H[8]; + int blkLen, i; +@@ -1696,6 +2303,7 @@ static void sha384(Guchar *msg, int msgL + hash[i*8 + 6] = (Guchar)(H[i] >> 8); + hash[i*8 + 7] = (Guchar)H[i]; + } ++#endif + } + + //------------------------------------------------------------------------ +@@ -1725,7 +2333,8 @@ static void revision6Hash(GooString *inp + //a.make the string K1 + memcpy(K1, inputPassword, inputPasswordLength); + memcpy(K1 + inputPasswordLength, K , KLength); +- memcpy(K1 + inputPasswordLength + KLength, userKey, userKeyLength); ++ if (userKey) ++ memcpy(K1 + inputPasswordLength + KLength, userKey, userKeyLength); + for(int i = 1; i < 64 ; ++i) { + memcpy(K1 + (i * sequenceLength),K1,sequenceLength); + } +@@ -1735,7 +2344,11 @@ static void revision6Hash(GooString *inp + memcpy(state.buf, state.cbc, 16); // Copy CBC IV to buf + state.bufIdx = 0; + state.paddingReached = gFalse; ++#ifdef ENABLE_NSS3 ++ state.context = aesInitContext(state.cbc, aesKey, 16, gFalse); ++#else + aesKeyExpansion(&state,aesKey,16,gFalse); ++#endif + + for(int i = 0; i < (4 * sequenceLength); i++) { + aesEncryptBlock(&state,K1 + (16 * i)); +@@ -1776,6 +2389,9 @@ static void revision6Hash(GooString *inp + sha512(E, totalLength, K); + } + rounds++; ++#ifdef ENABLE_NSS3 ++ PK11_DestroyContext(state.context, PR_TRUE); ++#endif + } + // the first 32 bytes of the final K are the output of the function. + } +diff --git a/poppler/Decrypt.h b/poppler/Decrypt.h +index d4667c8c..16fa9830 100644 +--- a/poppler/Decrypt.h ++++ b/poppler/Decrypt.h +@@ -31,6 +32,9 @@ + #include "goo/GooString.h" + #include "Object.h" + #include "Stream.h" ++#ifdef ENABLE_NSS3 ++#include ++#endif + + //------------------------------------------------------------------------ + // Decrypt +@@ -73,13 +77,21 @@ private: + * previous output is kept in buf. The paddingReached field is only used in + * case of encryption. */ + struct DecryptRC4State { ++#ifdef ENABLE_NSS3 ++ PK11Context *context; ++#else + Guchar state[256]; + Guchar x, y; ++#endif + }; + + struct DecryptAESState { ++#ifdef ENABLE_NSS3 ++ PK11Context *context; ++#else + Guint w[44]; + Guchar state[16]; ++#endif + Guchar cbc[16]; + Guchar buf[16]; + GBool paddingReached; // encryption only +@@ -87,8 +99,12 @@ struct DecryptAESState { + }; + + struct DecryptAES256State { ++#ifdef ENABLE_NSS3 ++ PK11Context *context; ++#else + Guint w[60]; + Guchar state[16]; ++#endif + Guchar cbc[16]; + Guchar buf[16]; + GBool paddingReached; // encryption only +diff --git a/poppler/poppler-config.h.cmake b/poppler/poppler-config.h.cmake +index f0a5a1a0..dcaade6f 100644 +--- a/poppler/poppler-config.h.cmake ++++ b/poppler/poppler-config.h.cmake +@@ -115,6 +115,12 @@ + #cmakedefine USE_CMS 1 + #endif + ++/* Build against libnss3 for digital signature validation and ++ implementation of encryption/decryption. */ ++#ifndef ENABLE_NSS3 ++#cmakedefine ENABLE_NSS3 1 ++#endif ++ + // Also, there are preprocessor symbols in the header files + // that are used but never defined when building poppler using configure + // or cmake: DISABLE_OUTLINE, DEBUG_MEM, +--- poppler-0.66.0/CMakeLists.txt ++++ poppler-0.66.0/CMakeLists.txt +@@ -490,7 +490,7 @@ add_library(poppler STATIC ${poppler_SRC + else() + add_library(poppler ${poppler_SRCS}) + endif() +-set_target_properties(poppler PROPERTIES VERSION 77.0.0 SOVERSION 77) ++set_target_properties(poppler PROPERTIES VERSION 78.0.0 SOVERSION 78) + if(MINGW) + get_target_property(POPPLER_SOVERSION poppler SOVERSION) + set_target_properties(poppler PROPERTIES SUFFIX "-${POPPLER_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}") diff --git a/SPECS/poppler.spec b/SPECS/poppler.spec index b52caf5..d2cf10d 100644 --- a/SPECS/poppler.spec +++ b/SPECS/poppler.spec @@ -4,7 +4,7 @@ Summary: PDF rendering library Name: poppler Version: 0.66.0 -Release: 20%{?dist} +Release: 26%{?dist} License: (GPLv2 or GPLv3) and GPLv2+ and LGPLv2+ and MIT URL: http://poppler.freedesktop.org/ Source0: http://poppler.freedesktop.org/poppler-%{version}.tar.xz @@ -70,6 +70,18 @@ Patch19: poppler-0.66.0-rescale-filter.patch # https://bugzilla.redhat.com/show_bug.cgi?id=1691724 Patch20: poppler-0.66.0-stack-overflow.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1618766 +Patch21: poppler-0.66.0-nss.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1713582 +Patch22: poppler-0.66.0-jpeg2000-component-size.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1732340 +Patch23: poppler-0.66.0-JPXStream-length.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1696636 +Patch24: poppler-0.66.0-PSOutputDev-rgb.patch + BuildRequires: cmake BuildRequires: gettext-devel BuildRequires: pkgconfig(cairo) @@ -187,6 +199,7 @@ export CC="gcc -fPIC" # hack to make the cmake call pass -DENABLE_LIBOPENJPEG=openjpeg2 \ -DENABLE_XPDF_HEADERS=ON \ -DENABLE_ZLIB=OFF \ + -DENABLE_NSS=ON \ .. unset CC make %{?_smp_mflags} @@ -222,7 +235,7 @@ test "$(pkg-config --modversion poppler-splash)" = "%{version}" %files %doc README %license COPYING -%{_libdir}/libpoppler.so.77* +%{_libdir}/libpoppler.so.78* %files devel %{_libdir}/pkgconfig/poppler.pc @@ -271,6 +284,32 @@ test "$(pkg-config --modversion poppler-splash)" = "%{version}" %{_mandir}/man1/* %changelog +* Tue Aug 13 2019 Marek Kasik - 0.66.0-26 +- Coverity scan related fixes +- Related: #1618766 + +* Tue Aug 13 2019 Marek Kasik - 0.66.0-25 +- Check whether input is RGB in PSOutputDev::checkPageSlice() +- also when using "-optimizecolorspace" flag +- Resolves: #1697576 + +* Fri Aug 9 2019 Marek Kasik - 0.66.0-24 +- Check whether input is RGB in PSOutputDev::checkPageSlice() +- Resolves: #1697576 + +* Fri Aug 9 2019 Marek Kasik - 0.66.0-23 +- Ignore dict Length if it is broken +- Resolves: #1733027 + +* Fri Aug 9 2019 Marek Kasik - 0.66.0-22 +- Fail gracefully if not all components of JPEG2000Stream +- have the same size +- Resolves: #1723505 + +* Fri Jun 28 2019 Marek Kasik - 0.66.0-21 +- Implement crypto functions using NSS +- Resolves: #1618766 + * Wed Apr 3 2019 Marek Kasik - 0.66.0-20 - Fix stack overflow on broken file - Resolves: #1691887