GH #2129: Add support for AES-GCM ciphers

This commit is contained in:
Günter Obiltschnig 2018-03-06 22:53:27 +01:00
parent e19f33351d
commit 943595c937
8 changed files with 138 additions and 12 deletions

View File

@ -70,7 +70,11 @@ public:
const ByteVec& key, const ByteVec& key,
const ByteVec& iv); const ByteVec& iv);
/// Creates a new CipherKeyImpl object using the given cipher /// Creates a new CipherKeyImpl object using the given cipher
/// name, key and initialization vector. /// name, key and initialization vector (IV).
///
/// The size of the IV must match the cipher's expected
/// IV size (see ivSize()), except for GCM mode, which allows
/// a custom IV size.
CipherKey(const std::string& name); CipherKey(const std::string& name);
/// Creates a new CipherKeyImpl object. Autoinitializes key and /// Creates a new CipherKeyImpl object. Autoinitializes key and
@ -105,6 +109,10 @@ public:
void setIV(const ByteVec& iv); void setIV(const ByteVec& iv);
/// Sets the initialization vector (IV) for the Cipher. /// Sets the initialization vector (IV) for the Cipher.
///
/// The size of the vector must match the cipher's expected
/// IV size (see ivSize()), except for GCM mode, which allows
/// a custom IV size.
CipherKeyImpl::Ptr impl(); CipherKeyImpl::Ptr impl();
/// Returns the impl object /// Returns the impl object

View File

@ -156,13 +156,6 @@ inline const CipherKeyImpl::ByteVec& CipherKeyImpl::getIV() const
} }
inline void CipherKeyImpl::setIV(const ByteVec& iv)
{
poco_assert(iv.size() == static_cast<ByteVec::size_type>(ivSize()));
_iv = iv;
}
inline const EVP_CIPHER* CipherKeyImpl::cipher() inline const EVP_CIPHER* CipherKeyImpl::cipher()
{ {
return _pCipher; return _pCipher;

View File

@ -50,6 +50,17 @@ public:
/// no padding is performed, the total amount of data encrypted or decrypted must then be a multiple of /// no padding is performed, the total amount of data encrypted or decrypted must then be a multiple of
/// the block size or an error will occur. /// the block size or an error will occur.
virtual std::string getTag(std::size_t tagSize = 16) const = 0;
/// Returns the GCM tag after encrypting using GCM mode.
///
/// Must be called after finalize().
virtual void setTag(const std::string& tag) = 0;
/// Sets the GCM tag for authenticated decryption using GCM mode.
///
/// Must be set before finalize() is called, otherwise
/// decryption will fail.
virtual std::streamsize transform( virtual std::streamsize transform(
const unsigned char* input, const unsigned char* input,
std::streamsize inputLength, std::streamsize inputLength,

View File

@ -15,6 +15,7 @@
#include "Poco/Crypto/CipherImpl.h" #include "Poco/Crypto/CipherImpl.h"
#include "Poco/Crypto/CryptoTransform.h" #include "Poco/Crypto/CryptoTransform.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include "Poco/Buffer.h"
#include <openssl/err.h> #include <openssl/err.h>
@ -60,8 +61,9 @@ namespace
~CryptoTransformImpl(); ~CryptoTransformImpl();
std::size_t blockSize() const; std::size_t blockSize() const;
int setPadding(int padding); int setPadding(int padding);
std::string getTag(std::size_t tagSize) const;
void setTag(const std::string& tag);
std::streamsize transform( std::streamsize transform(
const unsigned char* input, const unsigned char* input,
@ -110,6 +112,18 @@ namespace
_iv.empty() ? 0 : &_iv[0], _iv.empty() ? 0 : &_iv[0],
(dir == DIR_ENCRYPT) ? 1 : 0); (dir == DIR_ENCRYPT) ? 1 : 0);
#endif #endif
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
if (_iv.size() != EVP_CIPHER_iv_length(_pCipher) && EVP_CIPHER_mode(_pCipher) == EVP_CIPH_GCM_MODE)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
int rc = EVP_CIPHER_CTX_ctrl(_pContext, EVP_CTRL_GCM_SET_IVLEN, _iv.size(), NULL);
#else
int rc = EVP_CIPHER_CTX_ctrl(&_context, EVP_CTRL_GCM_SET_IVLEN, _iv.size(), NULL);
#endif
if (rc == 0) throwError();
}
#endif
} }
@ -144,6 +158,36 @@ namespace
} }
std::string CryptoTransformImpl::getTag(std::size_t tagSize) const
{
std::string tag;
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
Poco::Buffer<char> buffer(tagSize);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
int rc = EVP_CIPHER_CTX_ctrl(_pContext, EVP_CTRL_GCM_GET_TAG, tagSize, buffer.begin());
#else
int rc = EVP_CIPHER_CTX_ctrl(&_context, EVP_CTRL_GCM_GET_TAG, tagSize, buffer.begin());
#endif
if (rc == 0) throwError();
tag.assign(buffer.begin(), tagSize);
#endif
return tag;
}
void CryptoTransformImpl::setTag(const std::string& tag)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
int rc = EVP_CIPHER_CTX_ctrl(_pContext, EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast<char*>(tag.data()));
#elif OPENSSL_VERSION_NUMBER >= 0x10000000L
int rc = EVP_CIPHER_CTX_ctrl(&_context, EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast<char*>(tag.data()));
#else
int rc = 0;
#endif
if (rc == 0) throwError();
}
std::streamsize CryptoTransformImpl::transform( std::streamsize CryptoTransformImpl::transform(
const unsigned char* input, const unsigned char* input,
std::streamsize inputLength, std::streamsize inputLength,

View File

@ -63,7 +63,7 @@ CipherKeyImpl::CipherKeyImpl(const std::string& name,
_key(key), _key(key),
_iv(iv) _iv(iv)
{ {
// dummy access to Cipherfactory so that the EVP lib is initilaized // dummy access to Cipherfactory so that the EVP lib is initialized
CipherFactory::defaultFactory(); CipherFactory::defaultFactory();
_pCipher = EVP_get_cipherbyname(name.c_str()); _pCipher = EVP_get_cipherbyname(name.c_str());
@ -115,6 +115,7 @@ CipherKeyImpl::Mode CipherKeyImpl::mode() const
case EVP_CIPH_OFB_MODE: case EVP_CIPH_OFB_MODE:
return MODE_OFB; return MODE_OFB;
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
case EVP_CIPH_CTR_MODE: case EVP_CIPH_CTR_MODE:
return MODE_CTR; return MODE_CTR;
@ -123,6 +124,7 @@ CipherKeyImpl::Mode CipherKeyImpl::mode() const
case EVP_CIPH_CCM_MODE: case EVP_CIPH_CCM_MODE:
return MODE_CCM; return MODE_CCM;
#endif
} }
throw Poco::IllegalStateException("Unexpected value of EVP_CIPHER_mode()"); throw Poco::IllegalStateException("Unexpected value of EVP_CIPHER_mode()");
} }
@ -212,4 +214,11 @@ int CipherKeyImpl::ivSize() const
} }
void CipherKeyImpl::setIV(const ByteVec& iv)
{
poco_assert(mode() == MODE_GCM || iv.size() == static_cast<ByteVec::size_type>(ivSize()));
_iv = iv;
}
} } // namespace Poco::Crypto } } // namespace Poco::Crypto

View File

@ -69,6 +69,8 @@ namespace
std::size_t blockSize() const; std::size_t blockSize() const;
std::size_t maxDataSize() const; std::size_t maxDataSize() const;
std::string getTag(std::size_t) const;
void setTag(const std::string&);
std::streamsize transform( std::streamsize transform(
const unsigned char* input, const unsigned char* input,
@ -127,6 +129,17 @@ namespace
} }
std::string RSAEncryptImpl::getTag(std::size_t) const
{
return std::string();
}
void RSAEncryptImpl::setTag(const std::string&)
{
}
std::streamsize RSAEncryptImpl::transform( std::streamsize RSAEncryptImpl::transform(
const unsigned char* input, const unsigned char* input,
std::streamsize inputLength, std::streamsize inputLength,
@ -192,6 +205,8 @@ namespace
~RSADecryptImpl(); ~RSADecryptImpl();
std::size_t blockSize() const; std::size_t blockSize() const;
std::string getTag(std::size_t) const;
void setTag(const std::string&);
std::streamsize transform( std::streamsize transform(
const unsigned char* input, const unsigned char* input,
@ -233,6 +248,17 @@ namespace
} }
std::string RSADecryptImpl::getTag(std::size_t) const
{
return std::string();
}
void RSADecryptImpl::setTag(const std::string&)
{
}
std::streamsize RSADecryptImpl::transform( std::streamsize RSADecryptImpl::transform(
const unsigned char* input, const unsigned char* input,
std::streamsize inputLength, std::streamsize inputLength,

View File

@ -16,6 +16,7 @@
#include "Poco/Crypto/CipherKey.h" #include "Poco/Crypto/CipherKey.h"
#include "Poco/Crypto/X509Certificate.h" #include "Poco/Crypto/X509Certificate.h"
#include "Poco/Crypto/CryptoStream.h" #include "Poco/Crypto/CryptoStream.h"
#include "Poco/Crypto/CryptoTransform.h"
#include "Poco/StreamCopier.h" #include "Poco/StreamCopier.h"
#include "Poco/Base64Encoder.h" #include "Poco/Base64Encoder.h"
#include "Poco/HexBinaryEncoder.h" #include "Poco/HexBinaryEncoder.h"
@ -188,6 +189,38 @@ void CryptoTest::testEncryptDecryptDESECB()
} }
void CryptoTest::testEncryptDecryptGCM()
{
CipherKey key("aes-256-gcm");
CipherKey::ByteVec iv(20, 213);
key.setIV(iv);
Cipher::Ptr pCipher = CipherFactory::defaultFactory().createCipher(key);
for (std::size_t n = 1; n < MAX_DATA_SIZE; n++)
{
std::stringstream str;
CryptoTransform* pEncryptor = pCipher->createEncryptor();
CryptoOutputStream encryptorStream(str, pEncryptor);
std::string in(n, 'x');
encryptorStream << in;
encryptorStream.close();
assert (encryptorStream.good());
std::string tag = pEncryptor->getTag();
CryptoTransform* pDecryptor = pCipher->createDecryptor();
pDecryptor->setTag(tag);
CryptoInputStream decryptorStream(str, pDecryptor);
std::string out;
decryptorStream >> out;
assert (in == out);
}
}
void CryptoTest::testPassword() void CryptoTest::testPassword()
{ {
CipherKey key("aes256", "password", "salt"); CipherKey key("aes256", "password", "salt");
@ -331,8 +364,9 @@ CppUnit::Test* CryptoTest::suite()
CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptWithSalt); CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptWithSalt);
CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptWithSaltSha1); CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptWithSaltSha1);
CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptDESECB); CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptDESECB);
CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptGCM);
CppUnit_addTest(pSuite, CryptoTest, testPassword); CppUnit_addTest(pSuite, CryptoTest, testPassword);
CppUnit_addTest(pSuite, CryptoTest,testPasswordSha1); CppUnit_addTest(pSuite, CryptoTest, testPasswordSha1);
CppUnit_addTest(pSuite, CryptoTest, testEncryptInterop); CppUnit_addTest(pSuite, CryptoTest, testEncryptInterop);
CppUnit_addTest(pSuite, CryptoTest, testDecryptInterop); CppUnit_addTest(pSuite, CryptoTest, testDecryptInterop);
CppUnit_addTest(pSuite, CryptoTest, testStreams); CppUnit_addTest(pSuite, CryptoTest, testStreams);

View File

@ -33,6 +33,7 @@ public:
void testEncryptDecryptWithSalt(); void testEncryptDecryptWithSalt();
void testEncryptDecryptWithSaltSha1(); void testEncryptDecryptWithSaltSha1();
void testEncryptDecryptDESECB(); void testEncryptDecryptDESECB();
void testEncryptDecryptGCM();
void testStreams(); void testStreams();
void testPassword(); void testPassword();
void testPasswordSha1(); void testPasswordSha1();