mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-13 10:32:57 +01:00
feat(Envelope): Add envelope to crypto #3561
This commit is contained in:
parent
c4fb51ac72
commit
e6afb8749c
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
174
Crypto/include/Poco/Crypto/Envelope.h
Normal file
174
Crypto/include/Poco/Crypto/Envelope.h
Normal 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
159
Crypto/src/Envelope.cpp
Normal 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
|
@ -19,7 +19,8 @@ endif
|
||||
|
||||
objects = CryptoTestSuite Driver \
|
||||
CryptoTest DigestEngineTest ECTest \
|
||||
EVPTest RSATest PKCS12ContainerTest
|
||||
EVPTest RSATest PKCS12ContainerTest \
|
||||
EnvelopeTest
|
||||
|
||||
target = testrunner
|
||||
target_version = 1
|
||||
|
@ -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;
|
||||
}
|
||||
|
147
Crypto/testsuite/src/EnvelopeTest.cpp
Normal file
147
Crypto/testsuite/src/EnvelopeTest.cpp
Normal 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;
|
||||
}
|
41
Crypto/testsuite/src/EnvelopeTest.h
Normal file
41
Crypto/testsuite/src/EnvelopeTest.h
Normal 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
|
Loading…
Reference in New Issue
Block a user