feat(Envelope): Add envelope to crypto #3561

This commit is contained in:
Alex Fabijanic 2022-04-13 10:02:05 +00:00
parent c4fb51ac72
commit e6afb8749c
9 changed files with 535 additions and 4 deletions

View File

@ -80,7 +80,14 @@
"unordered_map": "cpp",
"unordered_set": "cpp",
"utility": "cpp",
"vector": "cpp"
"vector": "cpp",
"*.tcc": "cpp",
"compare": "cpp",
"concepts": "cpp",
"memory_resource": "cpp",
"random": "cpp",
"ranges": "cpp",
"cfenv": "cpp"
},
"files.exclude": {
"**/.dep": true,

View File

@ -10,7 +10,7 @@ SYSLIBS += -lssl -lcrypto
objects = Cipher CipherFactory CipherImpl CipherKey CipherKeyImpl \
CryptoException CryptoStream CryptoTransform \
ECDSADigestEngine ECKey ECKeyImpl \
ECDSADigestEngine ECKey ECKeyImpl Envelope \
EVPCipherImpl EVPPKey KeyPair KeyPairImpl PKCS12Container \
RSACipherImpl RSAKey RSAKeyImpl RSADigestEngine DigestEngine \
X509Certificate OpenSSLInitializer

View File

@ -169,7 +169,7 @@ public:
static EVP_PKEY* duplicate(const EVP_PKEY* pFromKey, EVP_PKEY** pToKey);
/// Duplicates pFromKey into *pToKey and returns
// the pointer to duplicated EVP_PKEY.
/// the pointer to duplicated EVP_PKEY.
private:
EVPPKey();

View File

@ -0,0 +1,174 @@
//
// Envelope.h
//
// Library: Crypto
// Package: Envelope
// Module: Envelope
//
// Definition of the Envelope class.
//
// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Crypto_Envelope_INCLUDED
#define Crypto_Envelope_INCLUDED
#include "Poco/Crypto/Crypto.h"
#include "Poco/Crypto/EVPPKey.h"
#include <vector>
#include <openssl/evp.h>
namespace Poco {
namespace Crypto {
class Crypto_API Envelope
/// Envelope encrypts/decrypts data using a symmetric key.
///
/// Encryption and decryption with asymmetric keys is computationally expensive.
/// To alleviate that, Envelope encrypts data using a symmetric session key;
/// the key is then itself asymmetrically encrypted using a public key.
/// It is also possible to encrypt the session key with multiple public keys,
/// so that the message can be sent to multiple recipients.
///
/// Each recipient decrypts the session with their private key; the session
/// key for the message decryption is the same for each recipient.
{
public:
using Byte = unsigned char;
using ByteVec = std::vector<Byte>;
using EVPPKeyVec = std::vector<EVPPKey>;
using EVP_PKEYVec = std::vector<EVP_PKEY*>;
using EncKeyVec = std::vector<ByteVec>;
Envelope() = delete;
Envelope(const EVPPKey& key, int cipherNID);
/// Creates a new Envelope object.
/// Initialization vector is automatically
/// generated.
Envelope(const EVPPKeyVec& keys, int cipherNID);
/// Creates a new Envelope object.
/// Initialization vector is automatically
/// generated.
~Envelope();
/// Destroys the Envelope.
const ByteVec& iv() const;
/// Returns the initialization vector.
void addKey(const EVPPKey& key);
/// Adds the key to the list of private keys.
const EncKeyVec& keys() const;
/// Returns encrypted symmetric keys.
int cipherNID() const;
/// Reurns the cipher NID.
const ByteVec& seal(const std::string& plainText);
/// Encrypts the given text and returns the encrypted text.
const ByteVec& seal(const ByteVec& plainData);
/// Encrypts the given data and returns the encrypted data.
const ByteVec& getContent() const;
/// Returns the encrypted content.
void setContent(const ByteVec& enc);
/// Sets the encrypted content.
ByteVec open(const EVPPKey& privKey, const ByteVec& encKeys, const ByteVec& iv = ByteVec());
/// Decrypts the stored encrypted data and returns it.
std::string openAsString(const EVPPKey& privKey, const ByteVec& encKeys, const ByteVec& iv = ByteVec());
/// Decrypts the stored encrypted data and returns it.
static std::string toString(const ByteVec& data);
/// Converts and returns string from ByteVec.
private:
Envelope(int cipherNID);
Envelope(int cipherNID, const ByteVec& iv);
int ivSize() const;
int blockSize() const;
void handleErrors(std::string&& msg);
const EVP_CIPHER* _pCipher;
EVP_CIPHER_CTX* _pCtx;
EVP_CIPHER_CTX* _pDecCtx;
ByteVec _iv;
EVP_PKEYVec _pubKeys;
EncKeyVec _encKeys;
std::vector<int> _encKeysSizes;
ByteVec _encContent;
};
inline int Envelope::ivSize() const
{
return EVP_CIPHER_iv_length(_pCipher);
}
inline const Envelope::ByteVec& Envelope::iv() const
{
return _iv;
}
inline int Envelope::blockSize() const
{
return EVP_CIPHER_block_size(_pCipher);
}
inline const Envelope::EncKeyVec& Envelope::keys() const
{
return _encKeys;
}
inline std::string Envelope::toString(const ByteVec& data)
{
return std::string(data.begin(), data.end());
}
inline std::string Envelope::openAsString(const EVPPKey& privKey, const ByteVec& encKey, const ByteVec& iv)
{
return toString(open(privKey, encKey, iv));
}
const Envelope::ByteVec& Envelope::getContent() const
{
return _encContent;
}
void Envelope::setContent(const ByteVec& enc)
{
_encContent = enc;
}
inline int Envelope::cipherNID() const
{
return EVP_CIPHER_nid(_pCipher);
}
} } // namespace Poco::Crypto
#endif // Crypto_Envelope_INCLUDED

159
Crypto/src/Envelope.cpp Normal file
View File

@ -0,0 +1,159 @@
//
// Envelope.cpp
//
// Library: Crypto
// Package: Envelope
// Module: Envelope
//
// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Crypto/Envelope.h"
namespace Poco {
namespace Crypto {
Envelope::Envelope(int cipherNID): _pCipher(EVP_get_cipherbynid(cipherNID)),
_pCtx(EVP_CIPHER_CTX_new())
{
poco_check_ptr(_pCipher);
poco_check_ptr(_pCtx);
if (1 != EVP_CIPHER_CTX_init(_pCtx))
handleErrors(std::string("Envelope():EVP_CIPHER_CTX_init()"));
_iv.resize(ivSize(), 0);
}
Envelope::Envelope(const EVPPKey& key, int cipherNID):
Envelope(cipherNID)
{
addKey(key);
}
Envelope::Envelope(const EVPPKeyVec& keys, int cipherNID):
Envelope(cipherNID)
{
for (const auto& k : keys) addKey(k);
}
Envelope::~Envelope()
{
for (auto& pK : _pubKeys)
EVP_PKEY_free(pK);
EVP_CIPHER_CTX_free(_pCtx);
}
void Envelope::addKey(const EVPPKey& key)
{
EVP_PKEY* pKey;
_pubKeys.push_back(EVPPKey::duplicate((const EVP_PKEY*)key, &pKey));
_encKeys.emplace_back(EVP_PKEY_size(_pubKeys.back()));
}
const Envelope::ByteVec& Envelope::seal(const ByteVec& plainData)
{
Byte* pEncKeys[_encKeys.size()] = {};
int encKeysSizes[_encKeys.size()] = {};
int i = 0;
for (const auto& k : _encKeys)
pEncKeys[i++] = new Byte[k.size()];
int noOfKeys = static_cast<int>(_pubKeys.size());
if (_encKeys.size() != EVP_SealInit(_pCtx, _pCipher, pEncKeys, encKeysSizes, &_iv[0], &_pubKeys[0], noOfKeys))
{
i = 0;
for (; i < _encKeys.size(); ++i) delete [] pEncKeys[i];
handleErrors(std::string("Envelope::seal():EVP_SealInit()"));
}
i = 0;
for (auto& k : _encKeys)
{
if (encKeysSizes[i] != k.size())
k.resize(encKeysSizes[i]);
std::memcpy(&k[0], pEncKeys[i], encKeysSizes[i]);
++i;
}
i = 0;
for (; i < _encKeys.size(); ++i) delete [] pEncKeys[i];
int cipherTextLen = 0, len = 0;
int plainDataSize = static_cast<int>(plainData.size());
_encContent.resize(plainDataSize + blockSize());
if (1 != EVP_SealUpdate(_pCtx, &_encContent[0], &len, &plainData[0], plainDataSize))
handleErrors(std::string("Envelope::seal():EVP_SealUpdate()"));
cipherTextLen = len;
poco_assert (cipherTextLen < _encContent.size());
if(1 != EVP_SealFinal(_pCtx, &_encContent[len], &len))
handleErrors(std::string("Envelope::seal():EVP_SealFinal()"));
cipherTextLen += len;
poco_assert (cipherTextLen <= _encContent.size());
_encContent.resize(cipherTextLen);
return _encContent;
}
const Envelope::ByteVec& Envelope::seal(const std::string& plainText)
{
return seal(ByteVec(plainText.begin(), plainText.end()));
}
Envelope::ByteVec Envelope::open(const EVPPKey& privKey, const ByteVec& encKey, const ByteVec& iv)
{
if (iv.size() > 0) _iv = iv;
int encContentLen = static_cast<int>(_encContent.size());
int blockSz = blockSize();
int mod = encContentLen % blockSz;
if (mod || (encContentLen < blockSz))
{
throw Poco::InvalidArgumentException(
Poco::format("Envelope::open(): bad encrypted buffer size: %z (must be N x %d)",
_encContent.size(), blockSz));
}
int encKeyLen = static_cast<int>(encKey.size());
EVP_PKEY* pKey = const_cast<EVP_PKEY*>((const EVP_PKEY*)privKey);
if (1 != EVP_OpenInit(_pCtx, _pCipher, &encKey[0], encKeyLen, &_iv[0], pKey))
handleErrors(std::string("Envelope::open():EVP_OpenInit()"));
ByteVec plainData(_encContent.size()+blockSz, 0);
int len = 0;
if(1 != EVP_OpenUpdate(_pCtx, &plainData[0], &len, &_encContent[0], encContentLen))
handleErrors(std::string("Envelope::open():EVP_OpenUpdate()"));
int totalLen = len;
if(1 != EVP_OpenFinal(_pCtx, &plainData[len], &len))
handleErrors(std::string("Envelope::open():EVP_OpenFinal()"));
totalLen += len;
plainData.resize(totalLen);
return plainData;
}
void Envelope::handleErrors(std::string&& msg)
{
unsigned long err;
while ((err = ERR_get_error()))
{
if (!msg.empty()) msg.append("\n");
msg.append(ERR_error_string(err, 0));
}
throw CryptoException(msg);
}
} } // namespace Poco::Crypto

View File

@ -19,7 +19,8 @@ endif
objects = CryptoTestSuite Driver \
CryptoTest DigestEngineTest ECTest \
EVPTest RSATest PKCS12ContainerTest
EVPTest RSATest PKCS12ContainerTest \
EnvelopeTest
target = testrunner
target_version = 1

View File

@ -21,6 +21,7 @@
#include "EVPTest.h"
#include "DigestEngineTest.h"
#include "PKCS12ContainerTest.h"
#include "EnvelopeTest.h"
CppUnit::Test* CryptoTestSuite::suite()
@ -33,5 +34,6 @@ CppUnit::Test* CryptoTestSuite::suite()
pSuite->addTest(EVPTest::suite());
pSuite->addTest(DigestEngineTest::suite());
pSuite->addTest(PKCS12ContainerTest::suite());
pSuite->addTest(EnvelopeTest::suite());
return pSuite;
}

View File

@ -0,0 +1,147 @@
//
// EnvelopeTest.cpp
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "EnvelopeTest.h"
#include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h"
#include "Poco/Crypto/Envelope.h"
#include "Poco/Crypto/EVPPKey.h"
#include <iostream>
using Poco::Crypto::Envelope;
using Poco::Crypto::EVPPKey;
EnvelopeTest::EnvelopeTest(const std::string& name): CppUnit::TestCase(name)
{
}
EnvelopeTest::~EnvelopeTest()
{
}
void EnvelopeTest::testOneKeyRSA()
{
try
{
EVPPKey key(EVP_PKEY_RSA, 1024);
Envelope env(key, NID_aes_256_cbc);
assertEqual(env.cipherNID(), NID_aes_256_cbc);
std::string dec = "let's encrypt some text";
env.seal(dec);
assertTrue(env.openAsString(key, env.keys()[0]) == dec);
}
catch(const Poco::Exception& e)
{
std::cerr << e.displayText() << '\n';
throw;
}
}
void EnvelopeTest::testMultiKeyRSA()
{
try
{
Envelope::EVPPKeyVec keyVec;
keyVec.emplace_back(EVP_PKEY_RSA, 1024);
keyVec.emplace_back(EVP_PKEY_RSA, 2048);
keyVec.emplace_back(EVP_PKEY_RSA, 1024);
Envelope env(keyVec, NID_aes_256_cbc);
assertEqual(env.cipherNID(), NID_aes_256_cbc);
std::string dec = "let's encrypt some text";
env.seal(dec);
int i = 0;
for (const auto& key : keyVec)
{
assertTrue(env.openAsString(key, env.keys()[i++]) == dec);
}
}
catch(const Poco::Exception& e)
{
std::cerr << e.displayText() << '\n';
throw;
}
}
void EnvelopeTest::testRSATransfer()
{
try
{
EVPPKey key(EVP_PKEY_RSA, 1024);
Envelope env1(key, NID_aes_256_cbc);
std::string dec = "let's encrypt some text";
Envelope env2(key, NID_aes_256_cbc);
env2.setContent(env1.seal(dec));
assertTrue(env2.openAsString(key, env1.keys()[0], env1.iv()) == dec);
}
catch(const Poco::Exception& e)
{
std::cerr << e.displayText() << '\n';
throw;
}
}
void EnvelopeTest::testRSAMultiTransfer()
{
try
{
Envelope::EVPPKeyVec keyVec;
keyVec.emplace_back(EVP_PKEY_RSA, 1024);
keyVec.emplace_back(EVP_PKEY_RSA, 2048);
keyVec.emplace_back(EVP_PKEY_RSA, 1024);
Envelope env0(keyVec, NID_aes_256_cbc);
std::string dec = "let's encrypt some text";
const Envelope::ByteVec enc = env0.seal(dec);
int i = 0;
for (const auto& key : keyVec)
{
Envelope env(key, NID_aes_256_cbc);
env.setContent(enc);
assertTrue(env.openAsString(key, env0.keys()[i++], env0.iv()) == dec);
}
}
catch(const Poco::Exception& e)
{
std::cerr << e.displayText() << '\n';
throw;
}
}
void EnvelopeTest::setUp()
{
}
void EnvelopeTest::tearDown()
{
}
CppUnit::Test* EnvelopeTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("EnvelopeTest");
CppUnit_addTest(pSuite, EnvelopeTest, testOneKeyRSA);
CppUnit_addTest(pSuite, EnvelopeTest, testMultiKeyRSA);
CppUnit_addTest(pSuite, EnvelopeTest, testRSATransfer);
CppUnit_addTest(pSuite, EnvelopeTest, testRSAMultiTransfer);
return pSuite;
}

View File

@ -0,0 +1,41 @@
//
// EnvelopeTest.h
//
// Definition of the EnvelopeTest class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef EnvelopeTest_INCLUDED
#define EnvelopeTest_INCLUDED
#include "Poco/Crypto/Crypto.h"
#include "CppUnit/TestCase.h"
class EnvelopeTest: public CppUnit::TestCase
{
public:
EnvelopeTest(const std::string& name);
~EnvelopeTest();
void testOneKeyRSA();
void testMultiKeyRSA();
void testRSATransfer();
void testRSAMultiTransfer();
void setUp();
void tearDown();
static CppUnit::Test* suite();
private:
};
#endif // EnvelopeTest_INCLUDED