CryptoTools: add HMAC, move crypto impl. details to CryptoImpl

Change-Id: I8edb24ee5d9595ef54bd49526b631baf8a7415b1
Reviewed-on: https://gerrit.libreoffice.org/56970
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
This commit is contained in:
Tomaž Vajngerl 2018-07-04 16:25:37 +02:00 committed by Tomaž Vajngerl
parent 0d0bf4132d
commit 6db3aeb6e6
4 changed files with 425 additions and 171 deletions

View file

@ -21,26 +21,41 @@
#define INCLUDED_OOX_CRYPTO_CRYPTTOOLS_HXX
#include <config_oox.h>
#if USE_TLS_OPENSSL
#include <openssl/evp.h>
#include <openssl/sha.h>
#endif // USE_TLS_OPENSSL
#if USE_TLS_NSS
#include <nss.h>
#include <pk11pub.h>
#include <sechash.h>
#endif // USE_TLS_NSS
#include <oox/dllapi.h>
#include <sal/types.h>
#include <vector>
#include <sal/types.h>
#include <memory>
namespace oox {
namespace core {
class Crypto
/** Rounds up the input to the nearest multiple
*
* For example:
* input 1, multiple 16 = 16
* input 16, multiple 16 = 16
* input 17, multiple 16 = 32
* input 31, multiple 16 = 32
*/
template<typename T>
T roundUp(T input, T multiple)
{
if (input % multiple == 0)
return input;
return ((input / multiple) * multiple) + multiple;
}
enum class CryptoHashType
{
SHA1,
SHA256,
SHA512
};
struct CryptoImpl;
class OOX_DLLPUBLIC Crypto
{
public:
enum CryptoType
@ -52,47 +67,24 @@ public:
};
protected:
#if USE_TLS_OPENSSL
EVP_CIPHER_CTX mContext;
#endif
#if USE_TLS_NSS
PK11Context* mContext;
SECItem* mSecParam;
PK11SymKey* mSymKey;
#endif
#if USE_TLS_OPENSSL
const EVP_CIPHER* getCipher(CryptoType type);
#endif
#if USE_TLS_NSS
void setupContext(
std::vector<sal_uInt8>& key,
std::vector<sal_uInt8>& iv,
CryptoType type,
CK_ATTRIBUTE_TYPE operation);
#endif
std::unique_ptr<CryptoImpl> mpImpl;
protected:
Crypto();
public:
virtual ~Crypto();
virtual sal_uInt32 update(
std::vector<sal_uInt8>& output,
std::vector<sal_uInt8>& input,
sal_uInt32 inputLength = 0) = 0;
};
class Decrypt : public Crypto
class OOX_DLLPUBLIC Decrypt : public Crypto
{
public:
Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type);
virtual sal_uInt32 update(
sal_uInt32 update(
std::vector<sal_uInt8>& output,
std::vector<sal_uInt8>& input,
sal_uInt32 inputLength = 0) override;
sal_uInt32 inputLength = 0);
static sal_uInt32 aes128ecb(
@ -102,17 +94,27 @@ public:
};
class Encrypt : public Crypto
class OOX_DLLPUBLIC Encrypt : public Crypto
{
public:
Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type);
virtual sal_uInt32 update(
sal_uInt32 update(
std::vector<sal_uInt8>& output,
std::vector<sal_uInt8>& input,
sal_uInt32 inputLength = 0) override;
sal_uInt32 inputLength = 0);
};
class OOX_DLLPUBLIC CryptoHash : public Crypto
{
sal_Int32 mnHashSize;
public:
CryptoHash(std::vector<sal_uInt8>& rKey, CryptoHashType eType);
bool update(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength = 0);
std::vector<sal_uInt8> finalize();
};
} // namespace core
} // namespace oox

View file

@ -16,6 +16,20 @@ $(eval $(call gb_CppunitTest_add_exception_objects,oox_crypto,\
$(eval $(call gb_CppunitTest_use_sdk_api,oox_crypto))
ifeq ($(TLS),OPENSSL)
$(eval $(call gb_CppunitTest_externals,oox_crypto,\
openssl \
openssl_headers \
))
else
ifeq ($(TLS),NSS)
$(eval $(call gb_CppunitTest_use_externals,oox_crypto,\
plc4 \
nss3 \
))
endif
endif
$(eval $(call gb_CppunitTest_use_libraries,oox_crypto,\
basegfx \
comphelper \
@ -68,6 +82,7 @@ $(eval $(call gb_CppunitTest_use_components,oox_crypto,\
unotools/util/utl \
uui/util/uui \
vcl/vcl.common \
sax/source/expatwrap/expwrap \
))

View file

@ -15,6 +15,7 @@
#include <tools/stream.hxx>
#include <unotools/streamwrap.hxx>
#include <oox/crypto/CryptTools.hxx>
#include <oox/crypto/Standard2007Engine.hxx>
#include <oox/helper/binaryinputstream.hxx>
#include <oox/helper/binaryoutputstream.hxx>
@ -24,13 +25,77 @@ using namespace css;
class CryptoTest : public CppUnit::TestFixture
{
public:
void testCryptoHash();
void testRoundUp();
void testStandard2007();
CPPUNIT_TEST_SUITE(CryptoTest);
CPPUNIT_TEST(testCryptoHash);
CPPUNIT_TEST(testRoundUp);
CPPUNIT_TEST(testStandard2007);
CPPUNIT_TEST_SUITE_END();
};
namespace
{
std::string toString(std::vector<sal_uInt8> const& aInput)
{
std::stringstream aStream;
for (auto const& aValue : aInput)
{
aStream << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(aValue);
}
return aStream.str();
}
}
void CryptoTest::testCryptoHash()
{
// Check examples from Wikipedia (https://en.wikipedia.org/wiki/HMAC)
OString aContentString("The quick brown fox jumps over the lazy dog");
std::vector<sal_uInt8> aContent(aContentString.getStr(),
aContentString.getStr() + aContentString.getLength());
std::vector<sal_uInt8> aKey = { 'k', 'e', 'y' };
{
oox::core::CryptoHash aCryptoHash(aKey, oox::core::CryptoHashType::SHA1);
aCryptoHash.update(aContent);
std::vector<sal_uInt8> aHash = aCryptoHash.finalize();
CPPUNIT_ASSERT_EQUAL(std::string("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"),
toString(aHash));
}
{
oox::core::CryptoHash aCryptoHash(aKey, oox::core::CryptoHashType::SHA256);
aCryptoHash.update(aContent);
std::vector<sal_uInt8> aHash = aCryptoHash.finalize();
CPPUNIT_ASSERT_EQUAL(
std::string("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"),
toString(aHash));
}
{
oox::core::CryptoHash aCryptoHash(aKey, oox::core::CryptoHashType::SHA512);
aCryptoHash.update(aContent);
std::vector<sal_uInt8> aHash = aCryptoHash.finalize();
CPPUNIT_ASSERT_EQUAL(
std::string("b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549"
"f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a"),
toString(aHash));
}
}
void CryptoTest::testRoundUp()
{
CPPUNIT_ASSERT_EQUAL(16, oox::core::roundUp(16, 16));
CPPUNIT_ASSERT_EQUAL(32, oox::core::roundUp(32, 16));
CPPUNIT_ASSERT_EQUAL(64, oox::core::roundUp(64, 16));
CPPUNIT_ASSERT_EQUAL(16, oox::core::roundUp(01, 16));
CPPUNIT_ASSERT_EQUAL(32, oox::core::roundUp(17, 16));
CPPUNIT_ASSERT_EQUAL(32, oox::core::roundUp(31, 16));
}
void CryptoTest::testStandard2007()
{
oox::core::Standard2007Engine aEngine;

View file

@ -12,57 +12,146 @@
#include <filter/msfilter/mscodec.hxx>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <o3tl/make_unique.hxx>
#if USE_TLS_OPENSSL
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#endif // USE_TLS_OPENSSL
#if USE_TLS_NSS
#include <nss.h>
#include <pk11pub.h>
#include <sechash.h>
#endif // USE_TLS_NSS
namespace oox {
namespace core {
Crypto::Crypto()
#if USE_TLS_NSS
#if USE_TLS_OPENSSL
struct CryptoImpl
{
std::unique_ptr<EVP_CIPHER_CTX> mpContext;
std::unique_ptr<HMAC_CTX> mpHmacContext;
CryptoImpl() = default;
void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
{
mpContext.reset(new EVP_CIPHER_CTX);
EVP_CIPHER_CTX_init(mpContext.get());
const EVP_CIPHER* cipher = getCipher(eType);
if (cipher == nullptr)
return;
if (iv.empty())
EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), 0);
else
EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), iv.data());
EVP_CIPHER_CTX_set_padding(mpContext.get(), 0);
}
void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
{
mpContext.reset(new EVP_CIPHER_CTX);
EVP_CIPHER_CTX_init(mpContext.get());
const EVP_CIPHER* pCipher = getCipher(eType);
if (pCipher == nullptr)
return;
const size_t nMinKeySize = EVP_CIPHER_key_length(pCipher);
if (key.size() < nMinKeySize)
key.resize(nMinKeySize, 0);
if (iv.empty())
EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), 0);
else
{
const size_t nMinIVSize = EVP_CIPHER_iv_length(pCipher);
if (iv.size() < nMinIVSize)
iv.resize(nMinIVSize, 0);
EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), iv.data());
}
EVP_CIPHER_CTX_set_padding(mpContext.get(), 0);
}
void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
{
mpHmacContext.reset(new HMAC_CTX);
HMAC_CTX_init(mpHmacContext.get());
const EVP_MD* aEvpMd;
switch (eType)
{
case CryptoHashType::SHA1:
aEvpMd = EVP_sha1(); break;
case CryptoHashType::SHA256:
aEvpMd = EVP_sha256(); break;
case CryptoHashType::SHA512:
aEvpMd = EVP_sha512(); break;
}
HMAC_Init(mpHmacContext.get(), rKey.data(), rKey.size(), aEvpMd);
}
~CryptoImpl()
{
if (mpContext)
EVP_CIPHER_CTX_cleanup(mpContext.get());
if (mpHmacContext)
HMAC_CTX_cleanup(mpHmacContext.get());
}
const EVP_CIPHER* getCipher(Crypto::CryptoType type)
{
switch(type)
{
case Crypto::CryptoType::AES_128_ECB:
return EVP_aes_128_ecb();
case Crypto::CryptoType::AES_128_CBC:
return EVP_aes_128_cbc();
case Crypto::CryptoType::AES_256_CBC:
return EVP_aes_256_cbc();
default:
break;
}
return nullptr;
}
};
#elif USE_TLS_NSS
struct CryptoImpl
{
PK11Context* mContext;
SECItem* mSecParam;
PK11SymKey* mSymKey;
PK11SlotInfo* mpSlot;
CryptoImpl()
: mContext(nullptr)
, mSecParam(nullptr)
, mSymKey(nullptr)
#endif
{
#if USE_TLS_NSS
// Initialize NSS, database functions are not needed
NSS_NoDB_Init(nullptr);
#endif // USE_TLS_NSS
}
Crypto::~Crypto()
~CryptoImpl()
{
#if USE_TLS_OPENSSL
EVP_CIPHER_CTX_cleanup( &mContext );
#endif
#if USE_TLS_NSS
if (mContext)
PK11_DestroyContext(mContext, PR_TRUE);
if (mSymKey)
PK11_FreeSymKey(mSymKey);
if (mSecParam)
SECITEM_FreeItem(mSecParam, PR_TRUE);
#endif
if (mpSlot)
PK11_FreeSlot(mpSlot);
}
#if USE_TLS_OPENSSL
const EVP_CIPHER* Crypto::getCipher(CryptoType type)
{
switch(type)
{
case AES_128_ECB:
return EVP_aes_128_ecb();
case AES_128_CBC:
return EVP_aes_128_cbc();
case AES_256_CBC:
return EVP_aes_256_cbc();
default:
break;
}
return NULL;
}
#endif
#if USE_TLS_NSS
void Crypto::setupContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type, CK_ATTRIBUTE_TYPE operation)
void setupCryptoContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType type, CK_ATTRIBUTE_TYPE operation)
{
CK_MECHANISM_TYPE mechanism = static_cast<CK_ULONG>(-1);
@ -78,14 +167,14 @@ void Crypto::setupContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& i
switch(type)
{
case AES_128_ECB:
case Crypto::CryptoType::AES_128_ECB:
mechanism = CKM_AES_ECB;
break;
case AES_128_CBC:
case Crypto::CryptoType::AES_128_CBC:
mechanism = CKM_AES_CBC;
pIvItem = &ivItem;
break;
case AES_256_CBC:
case Crypto::CryptoType::AES_256_CBC:
mechanism = CKM_AES_CBC;
pIvItem = &ivItem;
break;
@ -93,9 +182,9 @@ void Crypto::setupContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& i
break;
}
PK11SlotInfo* pSlot(PK11_GetBestSlot(mechanism, nullptr));
mpSlot = PK11_GetBestSlot(mechanism, nullptr);
if (!pSlot)
if (!mpSlot)
throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
SECItem keyItem;
@ -103,14 +192,63 @@ void Crypto::setupContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& i
keyItem.data = key.data();
keyItem.len = key.size();
mSymKey = PK11_ImportSymKey(pSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr);
mSymKey = PK11_ImportSymKey(mpSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr);
if (!mSymKey)
throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
mSecParam = PK11_ParamFromIV(mechanism, pIvItem);
mContext = PK11_CreateContextBySymKey(mechanism, operation, mSymKey, mSecParam);
}
#endif // USE_TLS_NSS
void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
{
CK_MECHANISM_TYPE aMechanism = static_cast<CK_ULONG>(-1);
switch(eType)
{
case CryptoHashType::SHA1:
aMechanism = CKM_SHA_1_HMAC;
break;
case CryptoHashType::SHA256:
aMechanism = CKM_SHA256_HMAC;
break;
case CryptoHashType::SHA512:
aMechanism = CKM_SHA512_HMAC;
break;
}
mpSlot = PK11_GetBestSlot(aMechanism, nullptr);
if (!mpSlot)
throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
SECItem aKeyItem;
aKeyItem.data = rKey.data();
aKeyItem.len = rKey.size();
mSymKey = PK11_ImportSymKey(mpSlot, aMechanism, PK11_OriginUnwrap, CKA_SIGN, &aKeyItem, nullptr);
if (!mSymKey)
throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
SECItem param;
param.data = nullptr;
param.len = 0;
mContext = PK11_CreateContextBySymKey(aMechanism, CKA_SIGN, mSymKey, &param);
}
};
#else
struct CryptoImpl
{};
#endif
Crypto::Crypto()
: mpImpl(o3tl::make_unique<CryptoImpl>())
{
}
Crypto::~Crypto()
{
}
// DECRYPT
@ -124,29 +262,11 @@ Decrypt::Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto
#endif
#if USE_TLS_OPENSSL
EVP_CIPHER_CTX_init(&mContext);
const EVP_CIPHER* cipher = getCipher(type);
const size_t nMinKeySize = EVP_CIPHER_key_length(cipher);
if (key.size() < nMinKeySize)
key.resize(nMinKeySize, 0);
if (iv.empty())
EVP_DecryptInit_ex(&mContext, cipher, nullptr, key.data(), 0);
else
{
const size_t nMinIVSize = EVP_CIPHER_iv_length(cipher);
if (iv.size() < nMinIVSize)
iv.resize(nMinIVSize, 0);
EVP_DecryptInit_ex(&mContext, cipher, nullptr, key.data(), iv.data());
}
EVP_CIPHER_CTX_set_padding(&mContext, 0);
mpImpl->setupDecryptContext(key, iv, type);
#endif
#if USE_TLS_NSS
setupContext(key, iv, type, CKA_DECRYPT);
mpImpl->setupCryptoContext(key, iv, type, CKA_DECRYPT);
#endif // USE_TLS_NSS
}
@ -163,11 +283,11 @@ sal_uInt32 Decrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8
#endif
#if USE_TLS_OPENSSL
(void)EVP_DecryptUpdate(&mContext, output.data(), &outputLength, input.data(), actualInputLength);
(void)EVP_DecryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
#endif // USE_TLS_OPENSSL
#if USE_TLS_NSS
(void)PK11_CipherOp( mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength );
(void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
#endif // USE_TLS_NSS
return static_cast<sal_uInt32>(outputLength);
@ -194,19 +314,9 @@ Encrypt::Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto
#endif
#if USE_TLS_OPENSSL
EVP_CIPHER_CTX_init(&mContext);
const EVP_CIPHER* cipher = getCipher(type);
if (iv.empty())
EVP_EncryptInit_ex(&mContext, cipher, nullptr, key.data(), 0);
else
EVP_EncryptInit_ex(&mContext, cipher, nullptr, key.data(), iv.data());
EVP_CIPHER_CTX_set_padding(&mContext, 0);
#endif
#if USE_TLS_NSS
setupContext(key, iv, type, CKA_ENCRYPT);
mpImpl->setupEncryptContext(key, iv, type);
#elif USE_TLS_NSS
mpImpl->setupCryptoContext(key, iv, type, CKA_ENCRYPT);
#endif // USE_TLS_NSS
}
@ -223,16 +333,78 @@ sal_uInt32 Encrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8
#endif
#if USE_TLS_OPENSSL
(void)EVP_EncryptUpdate(&mContext, output.data(), &outputLength, input.data(), actualInputLength);
(void)EVP_EncryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
#endif // USE_TLS_OPENSSL
#if USE_TLS_NSS
(void)PK11_CipherOp(mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
(void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
#endif // USE_TLS_NSS
return static_cast<sal_uInt32>(outputLength);
}
// CryptoHash - HMAC
namespace
{
sal_Int32 getSizeForHashType(CryptoHashType eType)
{
switch (eType)
{
case CryptoHashType::SHA1: return 20;
case CryptoHashType::SHA256: return 32;
case CryptoHashType::SHA512: return 64;
}
return 0;
}
} // end anonymous namespace
CryptoHash::CryptoHash(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
: Crypto()
, mnHashSize(getSizeForHashType(eType))
{
#if USE_TLS_OPENSSL
mpImpl->setupCryptoHashContext(rKey, eType);
#elif USE_TLS_NSS
mpImpl->setupCryptoHashContext(rKey, eType);
PK11_DigestBegin(mpImpl->mContext);
#else
(void)rKey;
#endif
}
bool CryptoHash::update(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength)
{
#if USE_TLS_OPENSSL + USE_TLS_NSS > 0
sal_uInt32 nActualInputLength = (nInputLength == 0 || nInputLength > rInput.size()) ? rInput.size() : nInputLength;
#else
(void)input;
(void)inputLength;
#endif
#if USE_TLS_OPENSSL
return HMAC_Update(mpImpl->mpHmacContext.get(), rInput.data(), nActualInputLength) != 0;
#elif USE_TLS_NSS
return PK11_DigestOp(mpImpl->mContext, rInput.data(), nActualInputLength) == SECSuccess;
#endif
}
std::vector<sal_uInt8> CryptoHash::finalize()
{
std::vector<sal_uInt8> aHash(mnHashSize, 0);
unsigned int nSizeWritten;
#if USE_TLS_OPENSSL
(void) HMAC_Final(mpImpl->mpHmacContext.get(), aHash.data(), &nSizeWritten) != 0;
#elif USE_TLS_NSS
PK11_DigestFinal(mpImpl->mContext, aHash.data(), &nSizeWritten, aHash.size());
#endif
(void)nSizeWritten;
return aHash;
}
} // namespace core
} // namespace oox