/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include #include #include #ifdef OPENSSL_IS_BORINGSSL #ifdef _MSC_VER AWS_SUPPRESS_WARNING_PUSH(4201) #else AWS_SUPPRESS_WARNING_PUSH("-Wpedantic") #endif #endif #include #ifdef OPENSSL_IS_BORINGSSL AWS_SUPPRESS_WARNING_POP #endif #include #include #include using namespace Aws::Utils; using namespace Aws::Utils::Crypto; namespace Aws { namespace Utils { namespace Crypto { namespace OpenSSL { /** * openssl with OPENSSL_VERSION_NUMBER < 0x10100003L made data type details unavailable * libressl use openssl with data type details available, but mandatorily set * OPENSSL_VERSION_NUMBER = 0x20000000L, insane! * https://github.com/aws/aws-sdk-cpp/pull/507/commits/2c99f1fe0c4b4683280caeb161538d4724d6a179 */ #if defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER == 0x20000000L) #undef OPENSSL_VERSION_NUMBER #if LIBRESSL_VERSION_NUMBER < 0x3050000fL #define OPENSSL_VERSION_NUMBER 0x1000107fL #else #define OPENSSL_VERSION_NUMBER 0x1010000fL #endif #endif #define OPENSSL_VERSION_LESS_1_1 (OPENSSL_VERSION_NUMBER < 0x10100003L) #define OPENSSL_VERSION_LESS_3_0 (OPENSSL_VERSION_NUMBER < 0x30000000L) #if !OPENSSL_VERSION_LESS_3_0 #include #endif #if OPENSSL_VERSION_LESS_1_1 static const char* OPENSSL_INTERNALS_TAG = "OpenSSLCallbackState"; static std::mutex* locks(nullptr); #endif GetTheLights getTheLights; void init_static_state() { #if OPENSSL_VERSION_LESS_1_1 || defined(OPENSSL_IS_BORINGSSL) ERR_load_crypto_strings(); #else OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS /*options*/ ,NULL /* OpenSSL init settings*/ ); #endif #if !(defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) OPENSSL_add_all_algorithms_noconf(); #endif #if OPENSSL_VERSION_LESS_1_1 if (!CRYPTO_get_locking_callback()) { locks = Aws::NewArray(static_cast(CRYPTO_num_locks()), OPENSSL_INTERNALS_TAG); CRYPTO_set_locking_callback(&locking_fn); } if (!CRYPTO_get_id_callback()) { CRYPTO_set_id_callback(&id_fn); } #endif RAND_poll(); } void cleanup_static_state() { #if OPENSSL_VERSION_LESS_1_1 if (CRYPTO_get_locking_callback() == &locking_fn) { CRYPTO_set_locking_callback(nullptr); assert(locks); Aws::DeleteArray(locks); locks = nullptr; } if (CRYPTO_get_id_callback() == &id_fn) { CRYPTO_set_id_callback(nullptr); } #endif } #if OPENSSL_VERSION_LESS_1_1 void locking_fn(int mode, int n, const char*, int) { if (mode & CRYPTO_LOCK) { locks[n].lock(); } else { locks[n].unlock(); } } unsigned long id_fn() { return static_cast(std::hash()(std::this_thread::get_id())); } #endif } static const char* OPENSSL_LOG_TAG = "OpenSSLCipher"; void SecureRandomBytes_OpenSSLImpl::GetBytes(unsigned char* buffer, size_t bufferSize) { if (!bufferSize) { return; } if (!buffer) { AWS_LOGSTREAM_FATAL(OPENSSL_LOG_TAG, "Secure Random Bytes generator can't generate: " << bufferSize << " bytes with nullptr buffer."); assert(buffer); return; } int success = RAND_bytes(buffer, static_cast(bufferSize)); if (success != 1) { m_failure = true; } } class OpensslCtxRAIIGuard { public: OpensslCtxRAIIGuard() { m_ctx = EVP_MD_CTX_create(); assert(m_ctx != nullptr); } ~OpensslCtxRAIIGuard() { EVP_MD_CTX_destroy(m_ctx); m_ctx = nullptr; } EVP_MD_CTX* getResource() { return m_ctx; } private: EVP_MD_CTX *m_ctx; }; MD5OpenSSLImpl::MD5OpenSSLImpl() { m_ctx = EVP_MD_CTX_create(); assert(m_ctx != nullptr); #if !defined(OPENSSL_IS_BORINGSSL) EVP_MD_CTX_set_flags(m_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); #endif EVP_DigestInit_ex(m_ctx, EVP_md5(), nullptr); } MD5OpenSSLImpl::~MD5OpenSSLImpl() { EVP_MD_CTX_destroy(m_ctx); m_ctx = nullptr; } HashResult MD5OpenSSLImpl::Calculate(const Aws::String& str) { OpensslCtxRAIIGuard guard; auto ctx = guard.getResource(); #if !defined(OPENSSL_IS_BORINGSSL) EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); #endif EVP_DigestInit_ex(ctx, EVP_md5(), nullptr); EVP_DigestUpdate(ctx, str.c_str(), str.size()); ByteBuffer hash(EVP_MD_size(EVP_md5())); EVP_DigestFinal(ctx, hash.GetUnderlyingData(), nullptr); return HashResult(std::move(hash)); } HashResult MD5OpenSSLImpl::Calculate(Aws::IStream& stream) { OpensslCtxRAIIGuard guard; auto ctx = guard.getResource(); #if !defined(OPENSSL_IS_BORINGSSL) EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); #endif EVP_DigestInit_ex(ctx, EVP_md5(), nullptr); auto currentPos = stream.tellg(); if (currentPos == -1) { currentPos = 0; stream.clear(); } stream.seekg(0, stream.beg); char streamBuffer[Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE]; while (stream.good()) { stream.read(streamBuffer, Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE); auto bytesRead = stream.gcount(); if (bytesRead > 0) { EVP_DigestUpdate(ctx, streamBuffer, static_cast(bytesRead)); } } stream.clear(); stream.seekg(currentPos, stream.beg); ByteBuffer hash(EVP_MD_size(EVP_md5())); EVP_DigestFinal(ctx, hash.GetUnderlyingData(), nullptr); return HashResult(std::move(hash)); } void MD5OpenSSLImpl::Update(unsigned char* buffer, size_t bufferSize) { EVP_DigestUpdate(m_ctx, buffer, bufferSize); } HashResult MD5OpenSSLImpl::GetHash() { ByteBuffer hash(EVP_MD_size(EVP_md5())); EVP_DigestFinal(m_ctx, hash.GetUnderlyingData(), nullptr); return HashResult(std::move(hash)); } Sha1OpenSSLImpl::Sha1OpenSSLImpl() { m_ctx = EVP_MD_CTX_create(); assert(m_ctx != nullptr); #if !defined(OPENSSL_IS_BORINGSSL) EVP_MD_CTX_set_flags(m_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); #endif EVP_DigestInit_ex(m_ctx, EVP_sha1(), nullptr); } Sha1OpenSSLImpl::~Sha1OpenSSLImpl() { EVP_MD_CTX_destroy(m_ctx); m_ctx = nullptr; } HashResult Sha1OpenSSLImpl::Calculate(const Aws::String& str) { OpensslCtxRAIIGuard guard; auto ctx = guard.getResource(); EVP_DigestInit_ex(ctx, EVP_sha1(), nullptr); EVP_DigestUpdate(ctx, str.c_str(), str.size()); ByteBuffer hash(EVP_MD_size(EVP_sha1())); EVP_DigestFinal(ctx, hash.GetUnderlyingData(), nullptr); return HashResult(std::move(hash)); } HashResult Sha1OpenSSLImpl::Calculate(Aws::IStream& stream) { OpensslCtxRAIIGuard guard; auto ctx = guard.getResource(); EVP_DigestInit_ex(ctx, EVP_sha1(), nullptr); auto currentPos = stream.tellg(); if (currentPos == -1) { currentPos = 0; stream.clear(); } stream.seekg(0, stream.beg); char streamBuffer[Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE]; while (stream.good()) { stream.read(streamBuffer, Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE); auto bytesRead = stream.gcount(); if (bytesRead > 0) { EVP_DigestUpdate(ctx, streamBuffer, static_cast(bytesRead)); } } stream.clear(); stream.seekg(currentPos, stream.beg); ByteBuffer hash(EVP_MD_size(EVP_sha1())); EVP_DigestFinal(ctx, hash.GetUnderlyingData(), nullptr); return HashResult(std::move(hash)); } void Sha1OpenSSLImpl::Update(unsigned char* buffer, size_t bufferSize) { EVP_DigestUpdate(m_ctx, buffer, bufferSize); } HashResult Sha1OpenSSLImpl::GetHash() { ByteBuffer hash(EVP_MD_size(EVP_sha1())); EVP_DigestFinal(m_ctx, hash.GetUnderlyingData(), nullptr); return HashResult(std::move(hash)); } Sha256OpenSSLImpl::Sha256OpenSSLImpl() { m_ctx = EVP_MD_CTX_create(); assert(m_ctx != nullptr); #if !defined(OPENSSL_IS_BORINGSSL) EVP_MD_CTX_set_flags(m_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); #endif EVP_DigestInit_ex(m_ctx, EVP_sha256(), nullptr); } Sha256OpenSSLImpl::~Sha256OpenSSLImpl() { EVP_MD_CTX_destroy(m_ctx); m_ctx = nullptr; } HashResult Sha256OpenSSLImpl::Calculate(const Aws::String& str) { OpensslCtxRAIIGuard guard; auto ctx = guard.getResource(); EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr); EVP_DigestUpdate(ctx, str.c_str(), str.size()); ByteBuffer hash(EVP_MD_size(EVP_sha256())); EVP_DigestFinal(ctx, hash.GetUnderlyingData(), nullptr); return HashResult(std::move(hash)); } HashResult Sha256OpenSSLImpl::Calculate(Aws::IStream& stream) { OpensslCtxRAIIGuard guard; auto ctx = guard.getResource(); EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr); auto currentPos = stream.tellg(); if (currentPos == -1) { currentPos = 0; stream.clear(); } stream.seekg(0, stream.beg); char streamBuffer[Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE]; while (stream.good()) { stream.read(streamBuffer, Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE); auto bytesRead = stream.gcount(); if (bytesRead > 0) { EVP_DigestUpdate(ctx, streamBuffer, static_cast(bytesRead)); } } stream.clear(); stream.seekg(currentPos, stream.beg); ByteBuffer hash(EVP_MD_size(EVP_sha256())); EVP_DigestFinal(ctx, hash.GetUnderlyingData(), nullptr); return HashResult(std::move(hash)); } void Sha256OpenSSLImpl::Update(unsigned char* buffer, size_t bufferSize) { EVP_DigestUpdate(m_ctx, buffer, bufferSize); } HashResult Sha256OpenSSLImpl::GetHash() { ByteBuffer hash(EVP_MD_size(EVP_sha256())); EVP_DigestFinal(m_ctx, hash.GetUnderlyingData(), nullptr); return HashResult(std::move(hash)); } class HMACRAIIGuard { public: HMACRAIIGuard() { #if OPENSSL_VERSION_LESS_1_1 m_ctx = Aws::New("AllocSha256HAMCOpenSSLContext"); #elif OPENSSL_VERSION_LESS_3_0 m_ctx = HMAC_CTX_new(); #else m_mac = EVP_MAC_fetch(NULL, "HMAC", NULL); m_ctx = EVP_MAC_CTX_new(m_mac); #endif assert(m_ctx != nullptr); } ~HMACRAIIGuard() { #if OPENSSL_VERSION_LESS_1_1 Aws::Delete(m_ctx); #elif OPENSSL_VERSION_LESS_3_0 HMAC_CTX_free(m_ctx); #else EVP_MAC_free(m_mac); EVP_MAC_CTX_free(m_ctx); #endif m_ctx = nullptr; } #if OPENSSL_VERSION_LESS_3_0 HMAC_CTX* getResource() { #else EVP_MAC_CTX* getResource() { #endif return m_ctx; } private: #if OPENSSL_VERSION_LESS_3_0 HMAC_CTX *m_ctx; #else EVP_MAC *m_mac; EVP_MAC_CTX *m_ctx; #endif }; HashResult Sha256HMACOpenSSLImpl::Calculate(const ByteBuffer& toSign, const ByteBuffer& secret) { unsigned int length = SHA256_DIGEST_LENGTH; ByteBuffer digest(length); memset(digest.GetUnderlyingData(), 0, length); HMACRAIIGuard guard; #if OPENSSL_VERSION_LESS_3_0 HMAC_CTX* m_ctx = guard.getResource(); #else EVP_MAC_CTX* m_ctx = guard.getResource(); #endif #if OPENSSL_VERSION_LESS_1_1 HMAC_CTX_init(m_ctx); #endif #if OPENSSL_VERSION_LESS_3_0 HMAC_Init_ex(m_ctx, secret.GetUnderlyingData(), static_cast(secret.GetLength()), EVP_sha256(), NULL); HMAC_Update(m_ctx, toSign.GetUnderlyingData(), toSign.GetLength()); HMAC_Final(m_ctx, digest.GetUnderlyingData(), &length); #else char sha256[] {"SHA256"}; OSSL_PARAM ossl_params[2]; ossl_params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, sha256, 0); ossl_params[1] = OSSL_PARAM_construct_end(); EVP_MAC_init(m_ctx, secret.GetUnderlyingData(), static_cast(secret.GetLength()), ossl_params); EVP_MAC_update(m_ctx, toSign.GetUnderlyingData(), toSign.GetLength()); EVP_MAC_final(m_ctx, digest.GetUnderlyingData(), NULL, length); #endif #if OPENSSL_VERSION_LESS_1_1 HMAC_CTX_cleanup(m_ctx); #elif OPENSSL_VERSION_LESS_3_0 HMAC_CTX_reset(m_ctx); #endif return HashResult(std::move(digest)); } void LogErrors(const char* logTag = OPENSSL_LOG_TAG) { unsigned long errorCode = ERR_get_error(); char errStr[256]; ERR_error_string_n(errorCode, errStr, 256); AWS_LOGSTREAM_ERROR(logTag, errStr); } OpenSSLCipher::OpenSSLCipher(const CryptoBuffer& key, size_t blockSizeBytes, bool ctrMode) : SymmetricCipher(key, blockSizeBytes, ctrMode), m_encryptor_ctx(nullptr), m_decryptor_ctx(nullptr) { Init(); } OpenSSLCipher::OpenSSLCipher(OpenSSLCipher&& toMove) : SymmetricCipher(std::move(toMove)), m_encryptor_ctx(nullptr), m_decryptor_ctx(nullptr) { Init(); EVP_CIPHER_CTX_copy(m_encryptor_ctx, toMove.m_encryptor_ctx); EVP_CIPHER_CTX_copy(m_decryptor_ctx, toMove.m_decryptor_ctx); EVP_CIPHER_CTX_cleanup(toMove.m_encryptor_ctx); EVP_CIPHER_CTX_cleanup(toMove.m_decryptor_ctx); } OpenSSLCipher::OpenSSLCipher(CryptoBuffer&& key, CryptoBuffer&& initializationVector, CryptoBuffer&& tag) : SymmetricCipher(std::move(key), std::move(initializationVector), std::move(tag)), m_encryptor_ctx(nullptr), m_decryptor_ctx(nullptr) { Init(); } OpenSSLCipher::OpenSSLCipher(const CryptoBuffer& key, const CryptoBuffer& initializationVector, const CryptoBuffer& tag) : SymmetricCipher(key, initializationVector, tag), m_encryptor_ctx(nullptr), m_decryptor_ctx(nullptr) { Init(); } OpenSSLCipher::~OpenSSLCipher() { Cleanup(); if (m_encryptor_ctx) { EVP_CIPHER_CTX_free(m_encryptor_ctx); m_encryptor_ctx = nullptr; } if (m_decryptor_ctx) { EVP_CIPHER_CTX_free(m_decryptor_ctx); m_decryptor_ctx = nullptr; } } void OpenSSLCipher::Init() { if (m_failure) { return; } if (!m_encryptor_ctx) { // EVP_CIPHER_CTX_init() will be called inside EVP_CIPHER_CTX_new(). m_encryptor_ctx = EVP_CIPHER_CTX_new(); assert(m_encryptor_ctx != nullptr); } else { // _init is the same as _reset after openssl 1.1 EVP_CIPHER_CTX_init(m_encryptor_ctx); } if (!m_decryptor_ctx) { // EVP_CIPHER_CTX_init() will be called inside EVP_CIPHER_CTX_new(). m_decryptor_ctx = EVP_CIPHER_CTX_new(); assert(m_decryptor_ctx != nullptr); } else { // _init is the same as _reset after openssl 1.1 EVP_CIPHER_CTX_init(m_decryptor_ctx); } m_emptyPlaintext = false; } CryptoBuffer OpenSSLCipher::EncryptBuffer(const CryptoBuffer& unEncryptedData) { if (m_failure) { AWS_LOGSTREAM_FATAL(OPENSSL_LOG_TAG, "Cipher not properly initialized for encryption. Aborting"); return CryptoBuffer(); } int lengthWritten = static_cast(unEncryptedData.GetLength() + (GetBlockSizeBytes() - 1)); CryptoBuffer encryptedText(static_cast( lengthWritten + (GetBlockSizeBytes() - 1))); if (!EVP_EncryptUpdate(m_encryptor_ctx, encryptedText.GetUnderlyingData(), &lengthWritten, unEncryptedData.GetUnderlyingData(), static_cast(unEncryptedData.GetLength()))) { m_failure = true; LogErrors(); return CryptoBuffer(); } if (static_cast(lengthWritten) < encryptedText.GetLength()) { return CryptoBuffer(encryptedText.GetUnderlyingData(), static_cast(lengthWritten)); } return encryptedText; } CryptoBuffer OpenSSLCipher::FinalizeEncryption() { if (m_failure) { AWS_LOGSTREAM_FATAL(OPENSSL_LOG_TAG, "Cipher not properly initialized for encryption finalization. Aborting"); return CryptoBuffer(); } CryptoBuffer finalBlock(GetBlockSizeBytes()); int writtenSize = 0; if (!EVP_EncryptFinal_ex(m_encryptor_ctx, finalBlock.GetUnderlyingData(), &writtenSize)) { m_failure = true; LogErrors(); return CryptoBuffer(); } return CryptoBuffer(finalBlock.GetUnderlyingData(), static_cast(writtenSize)); } CryptoBuffer OpenSSLCipher::DecryptBuffer(const CryptoBuffer& encryptedData) { if (m_failure) { AWS_LOGSTREAM_FATAL(OPENSSL_LOG_TAG, "Cipher not properly initialized for decryption. Aborting"); return CryptoBuffer(); } int lengthWritten = static_cast(encryptedData.GetLength() + (GetBlockSizeBytes() - 1)); CryptoBuffer decryptedText(static_cast(lengthWritten)); if (!EVP_DecryptUpdate(m_decryptor_ctx, decryptedText.GetUnderlyingData(), &lengthWritten, encryptedData.GetUnderlyingData(), static_cast(encryptedData.GetLength()))) { m_failure = true; LogErrors(); return CryptoBuffer(); } if (lengthWritten == 0) { m_emptyPlaintext = true; } if (static_cast(lengthWritten) < decryptedText.GetLength()) { return CryptoBuffer(decryptedText.GetUnderlyingData(), static_cast(lengthWritten)); } return decryptedText; } CryptoBuffer OpenSSLCipher::FinalizeDecryption() { if (m_failure) { AWS_LOGSTREAM_FATAL(OPENSSL_LOG_TAG, "Cipher not properly initialized for decryption finalization. Aborting"); return CryptoBuffer(); } CryptoBuffer finalBlock(GetBlockSizeBytes()); int writtenSize = static_cast(finalBlock.GetLength()); int ret = EVP_DecryptFinal_ex(m_decryptor_ctx, finalBlock.GetUnderlyingData(), &writtenSize); #if !defined(OPENSSL_IS_AWSLC) && OPENSSL_VERSION_NUMBER > 0x1010104fL //1.1.1d if (ret <= 0) #else if (ret <= 0 && !m_emptyPlaintext) // see details why making exception for empty string at: https://github.com/aws/aws-sdk-cpp/issues/1413 #endif { m_failure = true; LogErrors(); return CryptoBuffer(); } return CryptoBuffer(finalBlock.GetUnderlyingData(), static_cast(writtenSize)); } void OpenSSLCipher::Reset() { Cleanup(); Init(); } void OpenSSLCipher::Cleanup() { m_failure = false; if (m_encryptor_ctx) EVP_CIPHER_CTX_cleanup(m_encryptor_ctx); if (m_decryptor_ctx) EVP_CIPHER_CTX_cleanup(m_decryptor_ctx); } bool OpenSSLCipher::CheckKeyAndIVLength(size_t expectedKeyLength, size_t expectedIVLength) { if (!m_failure && ((m_key.GetLength() != expectedKeyLength) || m_initializationVector.GetLength() != expectedIVLength)) { AWS_LOGSTREAM_ERROR(OPENSSL_LOG_TAG, "Expected Key size is: " << expectedKeyLength << " and expected IV size is: " << expectedIVLength); m_failure = true; } return !m_failure; } size_t AES_CBC_Cipher_OpenSSL::BlockSizeBytes = 16; size_t AES_CBC_Cipher_OpenSSL::KeyLengthBits = 256; static const char* CBC_LOG_TAG = "AES_CBC_Cipher_OpenSSL"; AES_CBC_Cipher_OpenSSL::AES_CBC_Cipher_OpenSSL(const CryptoBuffer& key) : OpenSSLCipher(key, BlockSizeBytes) { InitCipher(); } AES_CBC_Cipher_OpenSSL::AES_CBC_Cipher_OpenSSL(CryptoBuffer&& key, CryptoBuffer&& initializationVector) : OpenSSLCipher(std::move(key), std::move(initializationVector)) { InitCipher(); } AES_CBC_Cipher_OpenSSL::AES_CBC_Cipher_OpenSSL(const CryptoBuffer& key, const CryptoBuffer& initializationVector) : OpenSSLCipher(key, initializationVector) { InitCipher(); } void AES_CBC_Cipher_OpenSSL::InitCipher() { if (m_failure || !CheckKeyAndIVLength(KeyLengthBits/8, BlockSizeBytes)) { return; } if (!EVP_EncryptInit_ex(m_encryptor_ctx, EVP_aes_256_cbc(), nullptr, m_key.GetUnderlyingData(), m_initializationVector.GetUnderlyingData()) || !EVP_DecryptInit_ex(m_decryptor_ctx, EVP_aes_256_cbc(), nullptr, m_key.GetUnderlyingData(), m_initializationVector.GetUnderlyingData())) { m_failure = true; LogErrors(CBC_LOG_TAG); } } size_t AES_CBC_Cipher_OpenSSL::GetBlockSizeBytes() const { return BlockSizeBytes; } size_t AES_CBC_Cipher_OpenSSL::GetKeyLengthBits() const { return KeyLengthBits; } void AES_CBC_Cipher_OpenSSL::Reset() { OpenSSLCipher::Reset(); InitCipher(); } size_t AES_CTR_Cipher_OpenSSL::BlockSizeBytes = 16; size_t AES_CTR_Cipher_OpenSSL::KeyLengthBits = 256; static const char* CTR_LOG_TAG = "AES_CTR_Cipher_OpenSSL"; AES_CTR_Cipher_OpenSSL::AES_CTR_Cipher_OpenSSL(const CryptoBuffer& key) : OpenSSLCipher(key, BlockSizeBytes, true) { InitCipher(); } AES_CTR_Cipher_OpenSSL::AES_CTR_Cipher_OpenSSL(CryptoBuffer&& key, CryptoBuffer&& initializationVector) : OpenSSLCipher(std::move(key), std::move(initializationVector)) { InitCipher(); } AES_CTR_Cipher_OpenSSL::AES_CTR_Cipher_OpenSSL(const CryptoBuffer& key, const CryptoBuffer& initializationVector) : OpenSSLCipher(key, initializationVector) { InitCipher(); } void AES_CTR_Cipher_OpenSSL::InitCipher() { if (m_failure || !CheckKeyAndIVLength(KeyLengthBits/8, BlockSizeBytes)) { return; } if (!(EVP_EncryptInit_ex(m_encryptor_ctx, EVP_aes_256_ctr(), nullptr, m_key.GetUnderlyingData(), m_initializationVector.GetUnderlyingData()) && EVP_CIPHER_CTX_set_padding(m_encryptor_ctx, 0)) || !(EVP_DecryptInit_ex(m_decryptor_ctx, EVP_aes_256_ctr(), nullptr, m_key.GetUnderlyingData(), m_initializationVector.GetUnderlyingData()) && EVP_CIPHER_CTX_set_padding(m_decryptor_ctx, 0))) { m_failure = true; LogErrors(CTR_LOG_TAG); } } size_t AES_CTR_Cipher_OpenSSL::GetBlockSizeBytes() const { return BlockSizeBytes; } size_t AES_CTR_Cipher_OpenSSL::GetKeyLengthBits() const { return KeyLengthBits; } void AES_CTR_Cipher_OpenSSL::Reset() { OpenSSLCipher::Reset(); InitCipher(); } size_t AES_GCM_Cipher_OpenSSL::BlockSizeBytes = 16; size_t AES_GCM_Cipher_OpenSSL::KeyLengthBits = 256; size_t AES_GCM_Cipher_OpenSSL::IVLengthBytes = 12; size_t AES_GCM_Cipher_OpenSSL::TagLengthBytes = 16; static const char* GCM_LOG_TAG = "AES_GCM_Cipher_OpenSSL"; AES_GCM_Cipher_OpenSSL::AES_GCM_Cipher_OpenSSL(const CryptoBuffer& key) : OpenSSLCipher(key, IVLengthBytes) { InitCipher(); } AES_GCM_Cipher_OpenSSL::AES_GCM_Cipher_OpenSSL(const CryptoBuffer& key, const CryptoBuffer* aad) : OpenSSLCipher(key, IVLengthBytes), m_aad(*aad) { InitCipher(); } AES_GCM_Cipher_OpenSSL::AES_GCM_Cipher_OpenSSL(CryptoBuffer&& key, CryptoBuffer&& initializationVector, CryptoBuffer&& tag, CryptoBuffer&& aad) : OpenSSLCipher(std::move(key), std::move(initializationVector), std::move(tag)), m_aad(std::move(aad)) { InitCipher(); } AES_GCM_Cipher_OpenSSL::AES_GCM_Cipher_OpenSSL(const CryptoBuffer& key, const CryptoBuffer& initializationVector, const CryptoBuffer& tag, const CryptoBuffer& aad) : OpenSSLCipher(key, initializationVector, tag), m_aad(std::move(aad)) { InitCipher(); } CryptoBuffer AES_GCM_Cipher_OpenSSL::FinalizeEncryption() { if (m_failure) { AWS_LOGSTREAM_FATAL(GCM_LOG_TAG, "Cipher not properly initialized for encryption finalization. Aborting"); return CryptoBuffer(); } int writtenSize = 0; CryptoBuffer finalBlock(GetBlockSizeBytes()); EVP_EncryptFinal_ex(m_encryptor_ctx, finalBlock.GetUnderlyingData(), &writtenSize); m_tag = CryptoBuffer(TagLengthBytes); if (!EVP_CIPHER_CTX_ctrl(m_encryptor_ctx, EVP_CTRL_GCM_GET_TAG, static_cast(m_tag.GetLength()), m_tag.GetUnderlyingData())) { m_failure = true; LogErrors(GCM_LOG_TAG); } return CryptoBuffer(); } void AES_GCM_Cipher_OpenSSL::InitCipher() { if (m_failure || !CheckKeyAndIVLength(KeyLengthBits/8, IVLengthBytes)) { return; } if (!(EVP_EncryptInit_ex(m_encryptor_ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr) && EVP_EncryptInit_ex(m_encryptor_ctx, nullptr, nullptr, m_key.GetUnderlyingData(), m_initializationVector.GetUnderlyingData()) && EVP_CIPHER_CTX_set_padding(m_encryptor_ctx, 0)) || !(EVP_DecryptInit_ex(m_decryptor_ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr) && EVP_DecryptInit_ex(m_decryptor_ctx, nullptr, nullptr, m_key.GetUnderlyingData(), m_initializationVector.GetUnderlyingData()) && EVP_CIPHER_CTX_set_padding(m_decryptor_ctx, 0))) { m_failure = true; LogErrors(GCM_LOG_TAG); return; } if (m_aad.GetLength() > 0) { int outLen = 0; if(!EVP_EncryptUpdate(m_encryptor_ctx, nullptr, &outLen, m_aad.GetUnderlyingData(), m_aad.GetLength()) || !EVP_DecryptUpdate(m_decryptor_ctx, nullptr, &outLen, m_aad.GetUnderlyingData(), m_aad.GetLength())) { m_failure = true; LogErrors(GCM_LOG_TAG); return; } } //tag should always be set in GCM decrypt mode if (m_tag.GetLength() > 0) { if (m_tag.GetLength() < TagLengthBytes) { AWS_LOGSTREAM_ERROR(GCM_LOG_TAG, "Illegal attempt to decrypt an AES GCM payload without a valid tag set: tag length=" << m_tag.GetLength()); m_failure = true; return; } if (!EVP_CIPHER_CTX_ctrl(m_decryptor_ctx, EVP_CTRL_GCM_SET_TAG, static_cast(m_tag.GetLength()), m_tag.GetUnderlyingData())) { m_failure = true; LogErrors(GCM_LOG_TAG); } } } size_t AES_GCM_Cipher_OpenSSL::GetBlockSizeBytes() const { return BlockSizeBytes; } size_t AES_GCM_Cipher_OpenSSL::GetKeyLengthBits() const { return KeyLengthBits; } size_t AES_GCM_Cipher_OpenSSL::GetTagLengthBytes() const { return TagLengthBytes; } void AES_GCM_Cipher_OpenSSL::Reset() { OpenSSLCipher::Reset(); InitCipher(); } size_t AES_KeyWrap_Cipher_OpenSSL::KeyLengthBits = 256; size_t AES_KeyWrap_Cipher_OpenSSL::BlockSizeBytes = 8; static const unsigned char INTEGRITY_VALUE = 0xA6; static const size_t MIN_CEK_LENGTH_BYTES = 128 / 8; static const char* KEY_WRAP_TAG = "AES_KeyWrap_Cipher_OpenSSL"; AES_KeyWrap_Cipher_OpenSSL::AES_KeyWrap_Cipher_OpenSSL(const CryptoBuffer& key) : OpenSSLCipher(key, 0) { InitCipher(); } CryptoBuffer AES_KeyWrap_Cipher_OpenSSL::EncryptBuffer(const CryptoBuffer& plainText) { if (!m_failure) { m_workingKeyBuffer = CryptoBuffer({&m_workingKeyBuffer, (CryptoBuffer*) &plainText}); } return CryptoBuffer(); } CryptoBuffer AES_KeyWrap_Cipher_OpenSSL::FinalizeEncryption() { if (m_failure) { AWS_LOGSTREAM_FATAL(KEY_WRAP_TAG, "Cipher not properly initialized for encryption finalization. Aborting"); return CryptoBuffer(); } if (m_workingKeyBuffer.GetLength() < MIN_CEK_LENGTH_BYTES) { AWS_LOGSTREAM_ERROR(KEY_WRAP_TAG, "Incorrect input length of " << m_workingKeyBuffer.GetLength()); m_failure = true; return CryptoBuffer(); } //the following is an in place implementation of //RFC 3394 using the alternate in-place implementation. //we use one in-place buffer instead of the copy at the end. //the one letter variable names are meant to directly reflect the variables in the RFC CryptoBuffer cipherText(m_workingKeyBuffer.GetLength() + BlockSizeBytes); //put the integrity check register in the first 8 bytes of the final buffer. memset(cipherText.GetUnderlyingData(), INTEGRITY_VALUE, BlockSizeBytes); unsigned char* a = cipherText.GetUnderlyingData(); //put the register buffer after the integrity check register memcpy(cipherText.GetUnderlyingData() + BlockSizeBytes, m_workingKeyBuffer.GetUnderlyingData(), m_workingKeyBuffer.GetLength()); unsigned char* r = cipherText.GetUnderlyingData() + BlockSizeBytes; int n = static_cast(m_workingKeyBuffer.GetLength() / BlockSizeBytes); //temporary encryption buffer CryptoBuffer b(BlockSizeBytes * 2); int outLen = static_cast(b.GetLength()); //concatenation buffer CryptoBuffer tempInput(BlockSizeBytes * 2); for (int j = 0; j <= 5; ++j) { for (int i = 1; i <= n; ++i) { //concat A and R[i], A should be most significant and then R[i] should be least significant. memcpy(tempInput.GetUnderlyingData(), a, BlockSizeBytes); memcpy(tempInput.GetUnderlyingData() + BlockSizeBytes, r, BlockSizeBytes); //encrypt the concatenated A and R[I] and store it in B if (!EVP_EncryptUpdate(m_encryptor_ctx, b.GetUnderlyingData(), &outLen, tempInput.GetUnderlyingData(), static_cast(tempInput.GetLength()))) { LogErrors(KEY_WRAP_TAG); m_failure = true; return CryptoBuffer(); } unsigned char t = static_cast((n * j) + i); //put the 64 MSB ^ T into A memcpy(a, b.GetUnderlyingData(), BlockSizeBytes); a[7] ^= t; //put the 64 LSB into R[i] memcpy(r, b.GetUnderlyingData() + BlockSizeBytes, BlockSizeBytes); //increment i -> R[i] r += BlockSizeBytes; } //reset R r = cipherText.GetUnderlyingData() + BlockSizeBytes; } return cipherText; } CryptoBuffer AES_KeyWrap_Cipher_OpenSSL::DecryptBuffer(const CryptoBuffer& cipherText) { if (!m_failure) { m_workingKeyBuffer = CryptoBuffer({&m_workingKeyBuffer, (CryptoBuffer*)&cipherText}); } return CryptoBuffer(); } CryptoBuffer AES_KeyWrap_Cipher_OpenSSL::FinalizeDecryption() { if (m_failure) { AWS_LOGSTREAM_FATAL(KEY_WRAP_TAG, "Cipher not properly initialized for decryption finalization. Aborting"); return CryptoBuffer(); } if (m_workingKeyBuffer.GetLength() < MIN_CEK_LENGTH_BYTES + BlockSizeBytes) { AWS_LOGSTREAM_ERROR(KEY_WRAP_TAG, "Incorrect input length of " << m_workingKeyBuffer.GetLength()); m_failure = true; return CryptoBuffer(); } //the following is an in place implementation of //RFC 3394 using the alternate in-place implementation. //we use one in-place buffer instead of the copy at the end. //the one letter variable names are meant to directly reflect the variables in the RFC CryptoBuffer plainText(m_workingKeyBuffer.GetLength() - BlockSizeBytes); memcpy(plainText.GetUnderlyingData(), m_workingKeyBuffer.GetUnderlyingData() + BlockSizeBytes, plainText.GetLength()); //integrity register should be the first 8 bytes of the cipher text unsigned char* a = m_workingKeyBuffer.GetUnderlyingData(); //in-place register is the plaintext. For decryption, start at the last array position (8 bytes before the end); unsigned char* r = plainText.GetUnderlyingData() + plainText.GetLength() - BlockSizeBytes; int n = static_cast(plainText.GetLength() / BlockSizeBytes); //temporary encryption buffer CryptoBuffer b(BlockSizeBytes * 10); int outLen = static_cast(b.GetLength()); //concatenation buffer CryptoBuffer tempInput(BlockSizeBytes * 2); for(int j = 5; j >= 0; --j) { for(int i = n; i >= 1; --i) { //concat //A ^ t memcpy(tempInput.GetUnderlyingData(), a, BlockSizeBytes); unsigned char t = static_cast((n * j) + i); tempInput[7] ^= t; //R[i] memcpy(tempInput.GetUnderlyingData() + BlockSizeBytes, r, BlockSizeBytes); //Decrypt the concatenated buffer if(!EVP_DecryptUpdate(m_decryptor_ctx, b.GetUnderlyingData(), &outLen, tempInput.GetUnderlyingData(), static_cast(tempInput.GetLength()))) { m_failure = true; LogErrors(KEY_WRAP_TAG); return CryptoBuffer(); } //set A to MSB 64 bits of decrypted result memcpy(a, b.GetUnderlyingData(), BlockSizeBytes); //set R[i] to LSB 64 bits of decrypted result memcpy(r, b.GetUnderlyingData() + BlockSizeBytes, BlockSizeBytes); //decrement i -> R[i] r -= BlockSizeBytes; } r = plainText.GetUnderlyingData() + plainText.GetLength() - BlockSizeBytes; } //here we perform the integrity check to make sure A == 0xA6A6A6A6A6A6A6A6 for(size_t i = 0; i < BlockSizeBytes; ++i) { if(a[i] != INTEGRITY_VALUE) { m_failure = true; AWS_LOGSTREAM_ERROR(KEY_WRAP_TAG, "Integrity check failed for key wrap decryption."); return CryptoBuffer(); } } return plainText; } void AES_KeyWrap_Cipher_OpenSSL::InitCipher() { if (m_failure || !CheckKeyAndIVLength(KeyLengthBits/8, 0)) { return; } if (!(EVP_EncryptInit_ex(m_encryptor_ctx, EVP_aes_256_ecb(), nullptr, m_key.GetUnderlyingData(), nullptr) && EVP_CIPHER_CTX_set_padding(m_encryptor_ctx, 0)) || !(EVP_DecryptInit_ex(m_decryptor_ctx, EVP_aes_256_ecb(), nullptr, m_key.GetUnderlyingData(), nullptr) && EVP_CIPHER_CTX_set_padding(m_decryptor_ctx, 0))) { m_failure = true; LogErrors(KEY_WRAP_TAG); } } void AES_KeyWrap_Cipher_OpenSSL::Reset() { m_workingKeyBuffer = CryptoBuffer(); OpenSSLCipher::Reset(); InitCipher(); } } } }