diff --git a/Crypto/include/Poco/Crypto/CipherKey.h b/Crypto/include/Poco/Crypto/CipherKey.h index 2ef9229fa..e3d7c70e5 100644 --- a/Crypto/include/Poco/Crypto/CipherKey.h +++ b/Crypto/include/Poco/Crypto/CipherKey.h @@ -61,7 +61,8 @@ public: CipherKey(const std::string& name, const std::string& passphrase, const std::string& salt = "", - int iterationCount = DEFAULT_ITERATION_COUNT); + int iterationCount = DEFAULT_ITERATION_COUNT, + const std::string& digest = "md5"); /// Creates a new CipherKeyImpl object using the given /// cipher name, passphrase, salt value and iteration count. diff --git a/Crypto/include/Poco/Crypto/CipherKeyImpl.h b/Crypto/include/Poco/Crypto/CipherKeyImpl.h index 1eef8d299..e1febd9fd 100644 --- a/Crypto/include/Poco/Crypto/CipherKeyImpl.h +++ b/Crypto/include/Poco/Crypto/CipherKeyImpl.h @@ -54,7 +54,8 @@ public: CipherKeyImpl(const std::string& name, const std::string& passphrase, const std::string& salt, - int iterationCount); + int iterationCount, + const std::string& digest); /// Creates a new CipherKeyImpl object, using /// the given cipher name, passphrase, salt value /// and iteration count. @@ -116,6 +117,7 @@ private: private: const EVP_CIPHER* _pCipher; + const EVP_MD* _pDigest; std::string _name; ByteVec _key; ByteVec _iv; diff --git a/Crypto/src/CipherKey.cpp b/Crypto/src/CipherKey.cpp index 2183ed76d..275edd1c8 100644 --- a/Crypto/src/CipherKey.cpp +++ b/Crypto/src/CipherKey.cpp @@ -19,8 +19,12 @@ namespace Poco { namespace Crypto { -CipherKey::CipherKey(const std::string& name, const std::string& passphrase, const std::string& salt, int iterationCount): - _pImpl(new CipherKeyImpl(name, passphrase, salt, iterationCount)) +CipherKey::CipherKey(const std::string& name, + const std::string& passphrase, + const std::string& salt, + int iterationCount, + const std::string &digest): + _pImpl(new CipherKeyImpl(name, passphrase, salt, iterationCount, digest)) { } diff --git a/Crypto/src/CipherKeyImpl.cpp b/Crypto/src/CipherKeyImpl.cpp index e5b0aa843..d2bf1e1c5 100644 --- a/Crypto/src/CipherKeyImpl.cpp +++ b/Crypto/src/CipherKeyImpl.cpp @@ -28,8 +28,10 @@ namespace Crypto { CipherKeyImpl::CipherKeyImpl(const std::string& name, const std::string& passphrase, const std::string& salt, - int iterationCount): + int iterationCount, + const std::string& digest): _pCipher(0), + _pDigest(0), _name(name), _key(), _iv() @@ -40,6 +42,12 @@ CipherKeyImpl::CipherKeyImpl(const std::string& name, if (!_pCipher) throw Poco::NotFoundException("Cipher " + name + " was not found"); + + _pDigest = EVP_get_digestbyname(digest.c_str()); + + if (!_pDigest) + throw Poco::NotFoundException("Digest " + name + " was not found"); + _key = ByteVec(keySize()); _iv = ByteVec(ivSize()); generateKey(passphrase, salt, iterationCount); @@ -50,6 +58,7 @@ CipherKeyImpl::CipherKeyImpl(const std::string& name, const ByteVec& key, const ByteVec& iv): _pCipher(0), + _pDigest(0), _name(name), _key(key), _iv(iv) @@ -65,6 +74,7 @@ CipherKeyImpl::CipherKeyImpl(const std::string& name, CipherKeyImpl::CipherKeyImpl(const std::string& name): _pCipher(0), + _pDigest(0), _name(name), _key(), _iv() @@ -157,7 +167,7 @@ void CipherKeyImpl::generateKey( // Now create the key and IV, using the MD5 digest algorithm. int keySize = EVP_BytesToKey( _pCipher, - EVP_md5(), + _pDigest ? _pDigest : EVP_md5(), (salt.empty() ? 0 : saltBytes), reinterpret_cast(password.data()), static_cast(password.size()), diff --git a/Crypto/testsuite/src/CryptoTest.cpp b/Crypto/testsuite/src/CryptoTest.cpp index 679e275bc..65bea53f0 100644 --- a/Crypto/testsuite/src/CryptoTest.cpp +++ b/Crypto/testsuite/src/CryptoTest.cpp @@ -18,6 +18,7 @@ #include "Poco/Crypto/CryptoStream.h" #include "Poco/StreamCopier.h" #include "Poco/Base64Encoder.h" +#include "Poco/HexBinaryEncoder.h" #include @@ -124,6 +125,39 @@ void CryptoTest::testEncryptDecryptWithSalt() } +void CryptoTest::testEncryptDecryptWithSaltSha1() +{ + Cipher::Ptr pCipher = CipherFactory::defaultFactory().createCipher( + CipherKey("aes256", "simplepwd", "Too much salt", 2000, "sha1")); + Cipher::Ptr pCipher2 = CipherFactory::defaultFactory().createCipher( + CipherKey("aes256", "simplepwd", "Too much salt", 2000, "sha1")); + + for (std::size_t n = 1; n < MAX_DATA_SIZE; n++) + { + std::string in(n, 'x'); + std::string out = pCipher->encryptString(in, Cipher::ENC_NONE); + std::string result = pCipher2->decryptString(out, Cipher::ENC_NONE); + assert (in == result); + } + + for (std::size_t n = 1; n < MAX_DATA_SIZE; n++) + { + std::string in(n, 'x'); + std::string out = pCipher->encryptString(in, Cipher::ENC_BASE64); + std::string result = pCipher2->decryptString(out, Cipher::ENC_BASE64); + assert (in == result); + } + + for (std::size_t n = 1; n < MAX_DATA_SIZE; n++) + { + std::string in(n, 'x'); + std::string out = pCipher->encryptString(in, Cipher::ENC_BINHEX); + std::string result = pCipher2->decryptString(out, Cipher::ENC_BINHEX); + assert (in == result); + } +} + + void CryptoTest::testEncryptDecryptDESECB() { Cipher::Ptr pCipher = CipherFactory::defaultFactory().createCipher(CipherKey("des-ecb", "password")); @@ -167,6 +201,33 @@ void CryptoTest::testPassword() } +void CryptoTest::testPasswordSha1() +{ + // the test uses 1 iteration, as the openssl executable does not allow to set a custom number + // of iterations + CipherKey key("aes256", "password", "saltsalt", 1, "sha1"); + + std::ostringstream keyStream; + Poco::HexBinaryEncoder hexKeyEnc(keyStream); + hexKeyEnc.write(reinterpret_cast(&key.getKey()[0]), key.keySize()); + hexKeyEnc.close(); + std::string hexKey = keyStream.str(); + + std::ostringstream ivStream; + Poco::HexBinaryEncoder hexIvEnc(ivStream); + hexIvEnc.write(reinterpret_cast(&key.getIV()[0]), key.ivSize()); + hexIvEnc.close(); + std::string hexIv = ivStream.str(); + + // got Hex value for key and iv using: + // openssl enc -e -a -md sha1 -aes256 -k password -S 73616c7473616c74 -P + // (where "salt" == 73616c74 in Hex, doubled for an 8 bytes salt, openssl padds the salt with 0 + // whereas Poco's implementation padds with the existing bytes using a modulo operation) + assert (hexIv == "c96049b0edc0b67af61ecc43d3de8898"); + assert (hexKey == "cab86dd6261710891e8cb56ee3625691a75df344f0bff4c12cf3596fc00b39c7"); +} + + void CryptoTest::testEncryptInterop() { Cipher::Ptr pCipher = CipherFactory::defaultFactory().createCipher(CipherKey("aes256", "password", "salt")); @@ -268,8 +329,10 @@ CppUnit::Test* CryptoTest::suite() CppUnit_addTest(pSuite, CryptoTest, testEncryptDecrypt); CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptWithSalt); + CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptWithSaltSha1); CppUnit_addTest(pSuite, CryptoTest, testEncryptDecryptDESECB); CppUnit_addTest(pSuite, CryptoTest, testPassword); + CppUnit_addTest(pSuite, CryptoTest,testPasswordSha1); CppUnit_addTest(pSuite, CryptoTest, testEncryptInterop); CppUnit_addTest(pSuite, CryptoTest, testDecryptInterop); CppUnit_addTest(pSuite, CryptoTest, testStreams); diff --git a/Crypto/testsuite/src/CryptoTest.h b/Crypto/testsuite/src/CryptoTest.h index c49ddac58..0fdca3841 100644 --- a/Crypto/testsuite/src/CryptoTest.h +++ b/Crypto/testsuite/src/CryptoTest.h @@ -31,9 +31,11 @@ public: void testEncryptDecrypt(); void testEncryptDecryptWithSalt(); + void testEncryptDecryptWithSaltSha1(); void testEncryptDecryptDESECB(); void testStreams(); void testPassword(); + void testPasswordSha1(); void testEncryptInterop(); void testDecryptInterop(); void testCertificate();