Skip to content

Commit

Permalink
feat(Envelope): Add envelope to crypto #3561
Browse files Browse the repository at this point in the history
  • Loading branch information
aleks-f committed Apr 13, 2022
1 parent c4fb51a commit e6afb87
Show file tree
Hide file tree
Showing 9 changed files with 535 additions and 4 deletions.
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion Crypto/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Crypto/include/Poco/Crypto/EVPPKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ class Crypto_API EVPPKey

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();
Expand Down
174 changes: 174 additions & 0 deletions Crypto/include/Poco/Crypto/Envelope.h
Original file line number Diff line number Diff line change
@@ -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 changes: 159 additions & 0 deletions Crypto/src/Envelope.cpp
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion Crypto/testsuite/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ endif

objects = CryptoTestSuite Driver \
CryptoTest DigestEngineTest ECTest \
EVPTest RSATest PKCS12ContainerTest
EVPTest RSATest PKCS12ContainerTest \
EnvelopeTest

target = testrunner
target_version = 1
Expand Down
2 changes: 2 additions & 0 deletions Crypto/testsuite/src/CryptoTestSuite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "EVPTest.h"
#include "DigestEngineTest.h"
#include "PKCS12ContainerTest.h"
#include "EnvelopeTest.h"


CppUnit::Test* CryptoTestSuite::suite()
Expand All @@ -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;
}
Loading

0 comments on commit e6afb87

Please sign in to comment.