Commit b1502076 authored by Marek Kasik's avatar Marek Kasik

Decrypt: Implement crypto functions using NSS

This commit moves DecryptStream and EncryptStream
classes to their own source files so that they can
have different implementations. Hash functions
md5(), sha256(), sha384() and sha512() are also placed
to these source files to allow different implementations.
There is internal implementation of them
in DecryptStream.{cc|h} and the NSS one
in DecryptStreamNSS.{cc|h}.

This commit also adds some new functions for usage
in functions Decrypt::makeFileKey(), Decrypt::makeFileKey2()
and revision6Hash().
These are rc4DecryptArray(), aesDecryptArray() and
aesEncryptArray().

setNSSDir() function had to be moved to its own source files
NSS.{cc|h} to have consistent initialization of NSS over
all functions which needs it.
parent e47daf60
Pipeline #58082 failed with stage
in 2 minutes and 33 seconds
......@@ -62,6 +62,7 @@ option(ENABLE_LIBCURL "Build libcurl based HTTP support." ON)
option(ENABLE_ZLIB "Build with zlib." ON)
option(ENABLE_ZLIB_UNCOMPRESS "Use zlib to uncompress flate streams (not totally safe)." OFF)
option(SPLASH_CMYK "Include support for CMYK rasterization." OFF)
option(USE_NSS_DECRYPT "Use NSS for decoding/encoding of streams." OFF)
option(USE_FLOAT "Use single precision arithmetic in the Splash backend" OFF)
option(BUILD_SHARED_LIBS "Build poppler as a shared library" ON)
if(WIN32)
......@@ -218,6 +219,9 @@ else()
message(FATAL_ERROR "Invalid ENABLE_LIBOPENJPEG value: ${ENABLE_LIBOPENJPEG}")
endif()
set(ENABLE_LIBOPENJPEG "${WITH_OPENJPEG}")
if(USE_NSS_DECRYPT AND NOT NSS3_FOUND)
message(FATAL_ERROR "Install NSS3 before trying to build poppler. You can also decide to use the internal decryption.")
endif()
if(ENABLE_CMS STREQUAL "lcms2")
find_package(LCMS2)
set(USE_CMS ${LCMS2_FOUND})
......@@ -459,6 +463,7 @@ endif()
if (ENABLE_NSS3)
set(poppler_SRCS ${poppler_SRCS}
poppler/SignatureHandler.cc
poppler/NSS.cc
)
if(${CMAKE_VERSION} VERSION_LESS "3.6.0")
set(poppler_LIBS ${poppler_LIBS} ${NSS3_LIBRARIES})
......@@ -476,6 +481,15 @@ else ()
poppler/JPXStream.cc
)
endif()
if (USE_NSS_DECRYPT)
set(poppler_SRCS ${poppler_SRCS}
poppler/DecryptStreamNSS.cc
)
else ()
set(poppler_SRCS ${poppler_SRCS}
poppler/DecryptStream.cc
)
endif()
if(USE_CMS)
set(poppler_LIBS ${poppler_LIBS} ${LCMS2_LIBRARIES})
endif()
......@@ -636,6 +650,15 @@ if(ENABLE_UNSTABLE_API_ABI_HEADERS)
poppler/JPXStream.h
DESTINATION include/poppler)
endif()
if(USE_NSS_DECRYPT)
install(FILES
poppler/DecryptStreamNSS.h
DESTINATION include/poppler)
else()
install(FILES
poppler/DecryptStream.h
DESTINATION include/poppler)
endif()
if(ENABLE_SPLASH)
install(FILES
poppler/SplashOutputDev.h
......@@ -734,6 +757,7 @@ show_end_message_yesno("use zlib uncompress" ENABLE_ZLIB_UNCOMPRESS)
show_end_message_yesno("use nss3" ENABLE_NSS3)
show_end_message_yesno("use curl" ENABLE_LIBCURL)
show_end_message_yesno("use libopenjpeg2" WITH_OPENJPEG)
show_end_message_yesno("use NSS for decryption" USE_NSS_DECRYPT)
show_end_message_yesno("use lcms2" USE_CMS)
show_end_message_yesno("use boost" Boost_FOUND)
show_end_message_yesno("command line utils" ENABLE_UTILS)
......
This diff is collapsed.
......@@ -19,6 +19,7 @@
// Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2013, 2018, 2019 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
// Copyright (C) 2019 Marek Kasik <mkasik@redhat.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
......@@ -65,36 +66,6 @@ private:
// Helper classes
//------------------------------------------------------------------------
/* DecryptRC4State, DecryptAESState, DecryptAES256State are named like this for
* historical reasons, but they're used for encryption too.
* In case of decryption, the cbc field in AES and AES-256 contains the previous
* input block or the CBC initialization vector (IV) if the stream has just been
* reset). In case of encryption, it always contains the IV, whereas the
* previous output is kept in buf. The paddingReached field is only used in
* case of encryption. */
struct DecryptRC4State {
unsigned char state[256];
unsigned char x, y;
};
struct DecryptAESState {
unsigned int w[44];
unsigned char state[16];
unsigned char cbc[16];
unsigned char buf[16];
bool paddingReached; // encryption only
int bufIdx;
};
struct DecryptAES256State {
unsigned int w[60];
unsigned char state[16];
unsigned char cbc[16];
unsigned char buf[16];
bool paddingReached; // encryption only
int bufIdx;
};
class BaseCryptStream : public FilterStream {
public:
......@@ -117,40 +88,6 @@ protected:
Goffset charactersRead; // so that getPos() can be correct
int nextCharBuff; // EOF means not read yet
bool autoDelete;
union {
DecryptRC4State rc4;
DecryptAESState aes;
DecryptAES256State aes256;
} state;
};
//------------------------------------------------------------------------
// EncryptStream / DecryptStream
//------------------------------------------------------------------------
class EncryptStream : public BaseCryptStream {
public:
EncryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA,
int keyLength, Ref ref);
~EncryptStream();
void reset() override;
int lookChar() override;
};
class DecryptStream : public BaseCryptStream {
public:
DecryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA,
int keyLength, Ref ref);
~DecryptStream();
void reset() override;
int lookChar() override;
};
//------------------------------------------------------------------------
extern void md5(const unsigned char *msg, int msgLen, unsigned char *digest);
#endif
This diff is collapsed.
//========================================================================
//
// DecryptStream.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2008 Julien Rebetez <julien@fhtagn.net>
// Copyright (C) 2009 David Benjamin <davidben@mit.edu>
// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
// Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2013, 2018 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
// Copyright (C) 2019 Marek Kasik <mkasik@redhat.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================
#ifndef DECRYPT_STREAM_H
#define DECRYPT_STREAM_H
#include "goo/GooString.h"
#include "Object.h"
#include "Stream.h"
#include "Decrypt.h"
//------------------------------------------------------------------------
// Helper classes
//------------------------------------------------------------------------
/* DecryptRC4State, DecryptAESState, DecryptAES256State are named like this for
* historical reasons, but they're used for encryption too.
* In case of decryption, the cbc field in AES and AES-256 contains the previous
* input block or the CBC initialization vector (IV) if the stream has just been
* reset). In case of encryption, it always contains the IV, whereas the
* previous output is kept in buf. The paddingReached field is only used in
* case of encryption. */
struct DecryptRC4State {
unsigned char state[256];
unsigned char x, y;
};
struct DecryptAESState {
unsigned int w[44];
unsigned char state[16];
unsigned char cbc[16];
unsigned char buf[16];
bool paddingReached; // encryption only
int bufIdx;
};
struct DecryptAES256State {
unsigned int w[60];
unsigned char state[16];
unsigned char cbc[16];
unsigned char buf[16];
bool paddingReached; // encryption only
int bufIdx;
};
/*
* This function decrypts/encrypts input array by RC4 algorithm.
*/
void rc4DecryptArray(const unsigned char *inputArray, const int inputArrayLen,
unsigned char *outputArray,
const unsigned char *key, const int keyLen);
/*
* This function decrypts input array by AES-128 or AES-256 algorithm depending
* on length of the key.
*/
void aesDecryptArray(const unsigned char *IV,
const unsigned char *inputArray, const int inputArrayLen,
unsigned char *outputArray,
const unsigned char *key, const int keyLen);
/*
* This function encrypts input array by AES-128 or AES-256 algorithm depending
* on length of the key.
*/
void aesEncryptArray(const unsigned char *IV,
const unsigned char *inputArray, const int inputArrayLen,
unsigned char *outputArray,
const unsigned char *key, const int keyLen);
//------------------------------------------------------------------------
// EncryptStream / DecryptStream
//------------------------------------------------------------------------
class EncryptStream : public BaseCryptStream {
public:
EncryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA,
int keyLength, int objNum, int objGen);
~EncryptStream();
void reset() override;
int lookChar() override;
protected:
union {
DecryptRC4State rc4;
DecryptAESState aes;
DecryptAES256State aes256;
} state;
};
class DecryptStream : public BaseCryptStream {
public:
DecryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA,
int keyLength, int objNum, int objGen);
~DecryptStream();
void reset() override;
int lookChar() override;
protected:
union {
DecryptRC4State rc4;
DecryptAESState aes;
DecryptAES256State aes256;
} state;
};
//------------------------------------------------------------------------
extern void md5(const unsigned char *msg, int msgLen, unsigned char *digest);
extern void sha256(unsigned char *msg, int msgLen, unsigned char *hash);
extern void sha384(unsigned char *msg, int msgLen, unsigned char *hash);
extern void sha512(unsigned char *msg, int msgLen, unsigned char *hash);
#endif
//========================================================================
//
// DecryptStreamNSS.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2008 Julien Rebetez <julien@fhtagn.net>
// Copyright (C) 2008, 2010, 2016-2019 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2009 Matthias Franz <matthias@ktug.or.kr>
// Copyright (C) 2009 David Benjamin <davidben@mit.edu>
// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
// Copyright (C) 2013, 2017 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2016 Alok Anand <alok4nand@gmail.com>
// Copyright (C) 2016 Thomas Freitag <Thomas.Freitag@alfa.de>
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
// Copyright (C) 2019 Marek Kasik <mkasik@redhat.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================
#include <config.h>
#include <cstdint>
#include <string.h>
#include <nss.h>
#include <prerror.h>
#include <sechash.h>
#include "goo/gmem.h"
#include "goo/grandom.h"
#include "DecryptStreamNSS.h"
#include "NSS.h"
#include "Error.h"
static PK11Context *rc4InitContext(const unsigned char *key, int keyLen);
static unsigned char rc4DecryptByte(PK11Context *context, unsigned char c);
static bool aesReadBlock(Stream *str, unsigned char *in, bool addPadding);
static PK11Context *aesInitContext(const unsigned char *in, const unsigned char *objKey, int objKeyLength, bool decrypt);
static void aesEncryptBlock(DecryptAESState *s, unsigned char *in);
static void aesDecryptBlock(DecryptAESState *s, unsigned char *in, bool last);
//------------------------------------------------------------------------
// Helper functions for direct decryption/encryption
//------------------------------------------------------------------------
void rc4DecryptArray(const unsigned char *inputArray, const int inputArrayLen,
unsigned char *outputArray,
const unsigned char *key, const int keyLen) {
PK11Context *context;
int i;
context = rc4InitContext(key, keyLen);
for (i = 0; i < inputArrayLen; ++i) {
outputArray[i] = rc4DecryptByte(context, inputArray[i]);
}
}
void aesDecryptArray(const unsigned char *IV,
const unsigned char *inputArray, const int inputArrayLen,
unsigned char *outputArray,
const unsigned char *key, const int keyLen)
{
DecryptAESState state;
int i;
state.context = aesInitContext(IV, key, keyLen, true);
if (state.context) {
for (i = 0; i < inputArrayLen / 16; i++) {
aesDecryptBlock(&state, (unsigned char *)(inputArray + i * 16), i < inputArrayLen / 16 - 1 ? false : true);
memcpy(outputArray + i * 16, state.buf, 16);
}
PK11_DestroyContext(state.context, PR_TRUE);
}
}
void aesEncryptArray(const unsigned char *IV,
const unsigned char *inputArray, const int inputArrayLen,
unsigned char *outputArray,
const unsigned char *key, const int keyLen)
{
DecryptAESState state;
int i;
state.context = aesInitContext(IV, key, keyLen, false);
if (state.context) {
for (i = 0; i < inputArrayLen / 16; i++) {
aesEncryptBlock(&state, (unsigned char *)(inputArray + i * 16));
memcpy(outputArray + i * 16, state.buf, 16);
}
PK11_DestroyContext(state.context, PR_TRUE);
}
}
//------------------------------------------------------------------------
// EncryptStream
//------------------------------------------------------------------------
EncryptStream::EncryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA,
int keyLength, int objNum, int objGen):
BaseCryptStream(strA, fileKey, algoA, keyLength, objNum, objGen)
{
// Fill the CBC initialization vector for AES and AES-256
if (algo == cryptAES || algo == cryptAES256)
grandom_fill(state.aes.cbc, 16);
switch (algo) {
case cryptRC4:
state.rc4.context = nullptr;
break;
case cryptAES:
case cryptAES256:
state.aes.context = nullptr;
break;
case cryptNone:
break;
}
setNSSDir({});
}
EncryptStream::~EncryptStream() {
switch (algo) {
case cryptRC4:
if (state.rc4.context)
PK11_DestroyContext(state.rc4.context, PR_TRUE);
break;
case cryptAES:
case cryptAES256:
if (state.aes.context)
PK11_DestroyContext(state.aes.context, PR_TRUE);
break;
default:
break;
}
}
void EncryptStream::reset() {
BaseCryptStream::reset();
switch (algo) {
case cryptRC4:
if (state.rc4.context)
PK11_DestroyContext(state.rc4.context, PR_TRUE);
state.rc4.context = rc4InitContext(objKey, objKeyLength);
break;
case cryptAES:
case cryptAES256:
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);
state.aes.bufIdx = 0;
state.aes.paddingReached = false;
break;
case cryptNone:
break;
}
}
int EncryptStream::lookChar() {
unsigned char in[16];
int c;
if (nextCharBuff != EOF)
return nextCharBuff;
c = EOF; // make gcc happy
switch (algo) {
case cryptRC4:
if ((c = str->getChar()) != EOF) {
// RC4 is XOR-based: the decryption algorithm works for encryption too
c = rc4DecryptByte(state.rc4.context, (unsigned char)c);
}
break;
case cryptAES:
case cryptAES256:
if (state.aes.bufIdx == 16 && !state.aes.paddingReached) {
state.aes.paddingReached = !aesReadBlock(str, in, true);
aesEncryptBlock(&state.aes, in);
}
if (state.aes.bufIdx == 16) {
c = EOF;
} else {
c = state.aes.buf[state.aes.bufIdx++];
}
break;
case cryptNone:
break;
}
return (nextCharBuff = c);
}
//------------------------------------------------------------------------
// DecryptStream
//------------------------------------------------------------------------
DecryptStream::DecryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA,
int keyLength, int objNum, int objGen):
BaseCryptStream(strA, fileKey, algoA, keyLength, objNum, objGen)
{
switch (algo) {
case cryptRC4:
state.rc4.context = nullptr;
break;
case cryptAES:
case cryptAES256:
state.aes.context = nullptr;
break;
case cryptNone:
break;
}
setNSSDir({});
}
DecryptStream::~DecryptStream() {
switch (algo) {
case cryptRC4:
if (state.rc4.context)
PK11_DestroyContext(state.rc4.context, PR_TRUE);
break;
case cryptAES:
case cryptAES256:
if (state.aes.context)
PK11_DestroyContext(state.aes.context, PR_TRUE);
break;
default:
break;
}
}
void DecryptStream::reset() {
int i;
BaseCryptStream::reset();
switch (algo) {
case cryptRC4:
if (state.rc4.context)
PK11_DestroyContext(state.rc4.context, PR_TRUE);
state.rc4.context = rc4InitContext(objKey, objKeyLength);
break;
case cryptAES:
case cryptAES256:
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);
state.aes.bufIdx = 16;
break;
case cryptNone:
break;
}
}
int DecryptStream::lookChar() {
unsigned char in[16];
int c;
if (nextCharBuff != EOF)
return nextCharBuff;
c = EOF; // make gcc happy
switch (algo) {
case cryptRC4:
if ((c = str->getChar()) != EOF) {
if (unlikely(state.rc4.context == nullptr))
c = EOF;
else
c = rc4DecryptByte(state.rc4.context, (unsigned char)c);
}
break;
case cryptAES:
case cryptAES256:
if (unlikely(state.aes.context == nullptr))
break;
if (state.aes.bufIdx == 16) {
if (aesReadBlock(str, in, false)) {
aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
}
}
if (state.aes.bufIdx == 16) {
c = EOF;
} else {
c = state.aes.buf[state.aes.bufIdx++];
}
break;
case cryptNone:
break;
}
return (nextCharBuff = c);
}
//------------------------------------------------------------------------
// RC4-compatible decryption
//------------------------------------------------------------------------
/*
* 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;