From 522745f99a1a54ae2fc226c48aab75884396374e Mon Sep 17 00:00:00 2001 From: Marek Kasik Date: Mon, 10 May 2021 15:45:43 +0200 Subject: [PATCH] Implement crypto functions using NSS Resolves: #1944726 --- poppler-21.01.0-nss.patch | 1055 +++++++++++++++++++++++++++++++++++++ poppler.spec | 9 +- 2 files changed, 1063 insertions(+), 1 deletion(-) create mode 100644 poppler-21.01.0-nss.patch diff --git a/poppler-21.01.0-nss.patch b/poppler-21.01.0-nss.patch new file mode 100644 index 0000000..bf96362 --- /dev/null +++ b/poppler-21.01.0-nss.patch @@ -0,0 +1,1055 @@ +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 unsigned char *key, int keyLen); ++static unsigned char rc4DecryptByte(PK11Context *context, unsigned char c); ++#else + static void rc4InitKey(const unsigned char *key, int keyLen, unsigned char *state); + static unsigned char rc4DecryptByte(unsigned char *state, unsigned char *x, unsigned char *y, unsigned char c); ++#endif + + static bool aesReadBlock(Stream *str, unsigned char *in, bool addPadding); + ++#ifdef ENABLE_NSS3 ++static PK11Context *aesInitContext(unsigned char *in, unsigned char *objKey, int objKeyLength, bool decrypt); ++#else + static void aesKeyExpansion(DecryptAESState *s, const unsigned char *objKey, int objKeyLen, bool decrypt); ++#endif + static void aesEncryptBlock(DecryptAESState *s, const unsigned char *in); + static void aesDecryptBlock(DecryptAESState *s, const unsigned char *in, bool last); + ++#ifndef ENABLE_NSS3 + static void aes256KeyExpansion(DecryptAES256State *s, const unsigned char *objKey, int objKeyLen, bool decrypt); ++#endif + static void aes256EncryptBlock(DecryptAES256State *s, const unsigned char *in); + static void aes256DecryptBlock(DecryptAES256State *s, const unsigned char *in, bool last); + +@@ -70,6 +86,31 @@ static const unsigned char 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 bool initNSS() { ++ if (NSS_IsInitialized()) { ++ return true; ++ } else { ++ if (NSS_NoDB_Init(".") != SECSuccess) { ++ error(errInternal, -1, "NSS initialization failed: {0:s}", ++ PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); ++ return false; ++ } else { ++ atexit(shutdownNSS); ++ return true; ++ } ++ } ++} ++#endif ++ + 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, + const GooString *ownerPassword, const GooString *userPassword, unsigned char *fileKey, bool encryptMetadata, bool *ownerPasswordOk) + { +@@ -80,13 +121,21 @@ bool Decrypt::makeFileKey(int encVersio + DecryptAES256State state; + unsigned char test[127 + 56], test2[32]; + GooString *userPassword2; +- unsigned char fState[256]; + unsigned char tmpKey[16]; +- unsigned char fx, fy; + int len, i, j; ++#ifdef ENABLE_NSS3 ++ PK11Context *rc4Context; ++#else ++ unsigned char fState[256]; ++ unsigned char fx, fy; ++#endif + + *ownerPasswordOk = false; + ++#ifdef ENABLE_NSS3 ++ initNSS(); ++#endif ++ + if (encRevision == 5 || encRevision == 6) { + + // check the owner password +@@ -115,14 +164,26 @@ bool Decrypt::makeFileKey(int encVersio + // test contains the initial SHA-256 hash input K. + revision6Hash(ownerPassword, test, userKey->c_str()); + } ++#ifndef ENABLE_NSS3 + aes256KeyExpansion(&state, test, 32, true); ++#endif + for (i = 0; i < 16; ++i) { + state.cbc[i] = 0; + } ++#ifdef ENABLE_NSS3 ++ state.context = aesInitContext(state.cbc, test, 32, true); ++ if (state.context) { ++#endif + aes256DecryptBlock(&state, (unsigned char *)ownerEnc->c_str(), false); + memcpy(fileKey, state.buf, 16); + aes256DecryptBlock(&state, (unsigned char *)ownerEnc->c_str() + 16, false); + memcpy(fileKey + 16, state.buf, 16); ++#ifdef ENABLE_NSS3 ++ PK11_DestroyContext(state.context, PR_TRUE); ++ } else { ++ return false; ++ } ++#endif + + *ownerPasswordOk = true; + return true; +@@ -156,14 +217,26 @@ bool 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, true); ++#endif + for (i = 0; i < 16; ++i) { + state.cbc[i] = 0; + } ++#ifdef ENABLE_NSS3 ++ state.context = aesInitContext(state.cbc, test, 32, true); ++ if (state.context) { ++#endif + aes256DecryptBlock(&state, (unsigned char *)userEnc->c_str(), false); + memcpy(fileKey, state.buf, 16); + aes256DecryptBlock(&state, (unsigned char *)userEnc->c_str() + 16, false); + memcpy(fileKey + 16, state.buf, 16); ++#ifdef ENABLE_NSS3 ++ PK11_DestroyContext(state.context, PR_TRUE); ++ } else { ++ return false; ++ } ++#endif + + return true; + } +@@ -189,22 +262,41 @@ bool 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->c_str(), 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 @@ bool Decrypt::makeFileKey2(int encVersi + { + unsigned char *buf; + unsigned char test[32]; +- unsigned char fState[256]; + unsigned char tmpKey[16]; +- unsigned char fx, fy; + int len, i, j; +- bool ok; ++ bool ok = true; ++#ifdef ENABLE_NSS3 ++ PK11Context *rc4Context; ++#else ++ unsigned char fState[256]; ++ unsigned char fx, fy; ++#endif + + // generate file key + buf = (unsigned char *)gmalloc(72 + fileID->getLength()); +@@ -273,28 +369,52 @@ bool 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 = false; ++ } ++#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->c_str(), 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 = false; ++ } ++#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->c_str(), fileID->getLength()); + md5(buf, 32 + fileID->getLength(), buf); +- ok = memcmp(test, buf, 16) == 0; ++ if (ok) ++ ok = memcmp(test, buf, 16) == 0; + } else { + ok = false; + } +@@ -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] = refA.num & 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,10 +488,33 @@ BaseCryptStream::BaseCryptStream(Stream + charactersRead = 0; + nextCharBuff = EOF; + autoDelete = true; ++ ++#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, ++ false); ++#else + aesKeyExpansion(&state.aes, objKey, objKeyLength, false); + memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf ++#endif + state.aes.bufIdx = 0; + state.aes.paddingReached = false; + 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, ++ false); ++#else + aes256KeyExpansion(&state.aes256, objKey, objKeyLength, false); + memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf ++#endif + state.aes256.bufIdx = 0; + state.aes256.paddingReached = false; + 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, (unsigned char)c); ++#else + c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (unsigned char)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, ++ true); ++#else + aesKeyExpansion(&state.aes, objKey, objKeyLength, true); + 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, ++ true); ++#else + aes256KeyExpansion(&state.aes256, objKey, objKeyLength, true); + 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, (unsigned char)c); ++#else + c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (unsigned char)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, false)) { + 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, false)) { + aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF); +@@ -576,7 +795,176 @@ 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 unsigned char *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; ++ unsigned char output[48]; // Buffer to hold 256 bit key + padding ++ unsigned char iv[16]; // Initialization vector for AES ++ unsigned int 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 unsigned char *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 unsigned char rc4DecryptByte(PK11Context *context, unsigned char c) { ++ unsigned char 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(const unsigned char *key, int keyLen, unsigned char *state) + { + unsigned char index1, index2; + unsigned char t; +@@ -609,6 +997,8 @@ static unsigned char rc4DecryptByte(unsigned char *sta + return c ^ state[(tx + ty) % 256]; + } + ++#endif ++ + //------------------------------------------------------------------------ + // AES decryption + //------------------------------------------------------------------------ +@@ -639,6 +1029,178 @@ static bool aesReadBlock(Stream *str, G + } + } + ++#ifdef ENABLE_NSS3 ++ ++static PK11Context *aesInitContext(unsigned char *in, unsigned char *objKey, ++ int objKeyLength, bool 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, const unsigned char *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, const unsigned char *in, bool 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, const unsigned char *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, const unsigned char *in, ++ bool 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 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, + 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, + 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, +@@ -1121,10 +1683,33 @@ static void aes256DecryptBlock(DecryptAE + } + } + ++#endif ++ + //------------------------------------------------------------------------ + // MD5 message digest + //------------------------------------------------------------------------ + ++#ifdef ENABLE_NSS3 ++static void hashFunc(const unsigned char *msg, int msgLen, unsigned char *hash, ++ HASH_HashType type) { ++ HASHContext *context; ++ unsigned int 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 unsigned long rotateLeft(unsigned long x, int r) + { +@@ -1151,8 +1736,13 @@ static inline unsigned long md5Round4(unsigned long a, + state->digest[15] = (unsigned char)(state->d >> 24); + } + ++#endif ++ + void md5(const unsigned char *msg, int msgLen, unsigned char *digest) + { ++#ifdef ENABLE_NSS3 ++ hashFunc(msg, msgLen, digest, HASH_AlgMD5); ++#else + if (msgLen < 0) { + return; + } +@@ -1296,12 +1886,14 @@ void md5(unsigned char *msg, int msgLen, unsigned char + for (int i = 0; i < 16; ++i) { + digest[i] = state.digest[i]; + } ++#endif + } + + //------------------------------------------------------------------------ + // SHA-256 hash + //------------------------------------------------------------------------ + ++#ifndef ENABLE_NSS3 + static const unsigned int sha256K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, +@@ -1400,9 +1992,13 @@ static void sha256HashBlock(unsigned char *blk, + H[6] += g; + H[7] += h; + } ++#endif + + static void sha256(unsigned char *msg, int msgLen, unsigned char *hash) + { ++#ifdef ENABLE_NSS3 ++ hashFunc(msg, msgLen, hash, HASH_AlgSHA256); ++#else + unsigned char blk[64]; + unsigned int H[8]; + int blkLen, i; +@@ -1453,7 +2049,10 @@ static void sha256(unsigned char *msg, int msgL + hash[i * 4 + 2] = (unsigned char)(H[i] >> 8); + hash[i * 4 + 3] = (unsigned char)H[i]; + } ++#endif + } ++ ++#ifndef ENABLE_NSS3 + //------------------------------------------------------------------------ + // SHA-512 hash (see FIPS 180-4) + //------------------------------------------------------------------------ +@@ -1557,9 +2156,13 @@ static void sha512HashBlock(unsigned char *blk, + H[6] += g; + H[7] += h; + } ++#endif + + static void sha512(unsigned char *msg, int msgLen, unsigned char *hash) + { ++#ifdef ENABLE_NSS3 ++ hashFunc(msg, msgLen, hash, HASH_AlgSHA512); ++#else + unsigned char blk[128]; + uint64_t H[8]; + int blkLen = 0, i; +@@ -1622,6 +2225,7 @@ static void sha512(unsigned char *msg, int msgL + hash[i * 8 + 6] = (unsigned char)(H[i] >> 8); + hash[i * 8 + 7] = (unsigned char)H[i]; + } ++#endif + } + + //------------------------------------------------------------------------ +@@ -1631,6 +2235,9 @@ static void sha512(unsigned char *msg, int msgL + // 2.A 384 bit message digest is obtained by truncating the final hash value. + static void sha384(unsigned char *msg, int msgLen, unsigned char *hash) + { ++#ifdef ENABLE_NSS3 ++ hashFunc(msg, msgLen, hash, HASH_AlgSHA384); ++#else + unsigned char blk[128]; + uint64_t H[8]; + int blkLen, i; +@@ -1696,6 +2303,7 @@ static void sha384(unsigned char *msg, int msgL + hash[i * 8 + 6] = (unsigned char)(H[i] >> 8); + hash[i * 8 + 7] = (unsigned char)H[i]; + } ++#endif + } + + //------------------------------------------------------------------------ +@@ -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 = false; ++#ifdef ENABLE_NSS3 ++ state.context = aesInitContext(state.cbc, aesKey, 16, false); ++#else + aesKeyExpansion(&state, aesKey, 16, false); ++#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,14 +77,22 @@ private: + * case of encryption. */ + struct DecryptRC4State + { ++#ifdef ENABLE_NSS3 ++ PK11Context *context; ++#else + unsigned char state[256]; + unsigned char x, y; ++#endif + }; + + struct DecryptAESState + { ++#ifdef ENABLE_NSS3 ++ PK11Context *context; ++#else + unsigned int w[44]; + unsigned char state[16]; ++#endif + unsigned char cbc[16]; + unsigned char buf[16]; + bool paddingReached; // encryption only +@@ -87,8 +99,12 @@ struct DecryptAESState { + + struct DecryptAES256State + { ++#ifdef ENABLE_NSS3 ++ PK11Context *context; ++#else + unsigned int w[60]; + unsigned char state[16]; ++#endif + unsigned char cbc[16]; + unsigned char buf[16]; + bool 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 HAVE_SPLASH 1 + #endif + ++/* Build against libnss3 for digital signature validation and ++ implementation of encryption/decryption. */ ++#ifndef ENABLE_NSS3 ++#cmakedefine ENABLE_NSS3 1 ++#endif ++ + //------------------------------------------------------------------------ + // version + //------------------------------------------------------------------------ diff --git a/poppler.spec b/poppler.spec index b3bc5f4..f526071 100644 --- a/poppler.spec +++ b/poppler.spec @@ -4,7 +4,7 @@ Summary: PDF rendering library Name: poppler Version: 21.01.0 -Release: 7%{?dist} +Release: 8%{?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 @@ -21,6 +21,9 @@ Patch2: %{name}-gcc11.patch Patch3: poppler-21.01.0-glib-introspection.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1944726 +Patch4: poppler-21.01.0-nss.patch + BuildRequires: make BuildRequires: cmake BuildRequires: gcc-c++ @@ -214,6 +217,10 @@ test "$(pkg-config --modversion poppler-qt5)" = "%{version}" %{_mandir}/man1/* %changelog +* Mon May 10 2021 Marek Kasik - 21.01.0-8 +- Implement crypto functions using NSS +- Resolves: #1944726 + * Fri Apr 16 2021 Mohan Boddu - 21.01.0-7 - Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937