diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:32:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:32:49 +0000 |
commit | 8053187731ae8e3eb368d8360989cf5fd6eed9f7 (patch) | |
tree | 32bada84ff5d7460cdf3934fcbdbe770d6afe4cd /src/lib/crypto/cipher_ossl.cpp | |
parent | Initial commit. (diff) | |
download | rnp-8053187731ae8e3eb368d8360989cf5fd6eed9f7.tar.xz rnp-8053187731ae8e3eb368d8360989cf5fd6eed9f7.zip |
Adding upstream version 0.17.0.upstream/0.17.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/crypto/cipher_ossl.cpp')
-rw-r--r-- | src/lib/crypto/cipher_ossl.cpp | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/lib/crypto/cipher_ossl.cpp b/src/lib/crypto/cipher_ossl.cpp new file mode 100644 index 0000000..bb81d48 --- /dev/null +++ b/src/lib/crypto/cipher_ossl.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <cassert> +#include <algorithm> + +#include "cipher_ossl.hpp" +#include "utils.h" +#include "types.h" +#include <openssl/err.h> + +static const id_str_pair cipher_mode_map[] = { + {PGP_CIPHER_MODE_CBC, "CBC"}, + {PGP_CIPHER_MODE_OCB, "OCB"}, + {0, NULL}, +}; + +static const id_str_pair cipher_map[] = { + {PGP_SA_AES_128, "AES-128"}, + {PGP_SA_AES_256, "AES-256"}, + {PGP_SA_IDEA, "IDEA"}, + {0, NULL}, +}; + +EVP_CIPHER_CTX * +Cipher_OpenSSL::create(pgp_symm_alg_t alg, + const std::string &name, + bool encrypt, + size_t tag_size, + bool disable_padding) +{ +#if !defined(ENABLE_IDEA) + if (alg == PGP_SA_IDEA) { + RNP_LOG("IDEA support has been disabled"); + return nullptr; + } +#endif +#if !defined(ENABLE_BLOWFISH) + if (alg == PGP_SA_BLOWFISH) { + RNP_LOG("Blowfish support has been disabled"); + return nullptr; + } +#endif +#if !defined(ENABLE_CAST5) + if (alg == PGP_SA_CAST5) { + RNP_LOG("CAST5 support has been disabled"); + return nullptr; + } +#endif + const EVP_CIPHER *cipher = EVP_get_cipherbyname(name.c_str()); + if (!cipher) { + RNP_LOG("Unsupported cipher: %s", name.c_str()); + return nullptr; + } + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) { + RNP_LOG("Failed to create cipher context: %lu", ERR_peek_last_error()); + return nullptr; + } + if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt ? 1 : 0) != 1) { + RNP_LOG("Failed to initialize cipher: %lu", ERR_peek_last_error()); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + // set tag size + if (encrypt && tag_size) { + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_size, NULL) != 1) { + RNP_LOG("Failed to set AEAD tag length: %lu", ERR_peek_last_error()); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + } + if (disable_padding) { + EVP_CIPHER_CTX_set_padding(ctx, 0); + } + return ctx; +} + +static std::string +make_name(pgp_symm_alg_t cipher, pgp_cipher_mode_t mode) +{ + const char *cipher_string = id_str_pair::lookup(cipher_map, cipher, NULL); + const char *mode_string = id_str_pair::lookup(cipher_mode_map, mode, NULL); + if (!cipher_string || !mode_string) { + return ""; + } + return std::string(cipher_string) + "-" + mode_string; +} + +std::unique_ptr<Cipher_OpenSSL> +Cipher_OpenSSL::encryption(pgp_symm_alg_t cipher, + pgp_cipher_mode_t mode, + size_t tag_size, + bool disable_padding) +{ + EVP_CIPHER_CTX *ossl_ctx = + create(cipher, make_name(cipher, mode), true, tag_size, disable_padding); + if (!ossl_ctx) { + return NULL; + } + return std::unique_ptr<Cipher_OpenSSL>(new (std::nothrow) + Cipher_OpenSSL(cipher, ossl_ctx, tag_size, true)); +} + +std::unique_ptr<Cipher_OpenSSL> +Cipher_OpenSSL::decryption(pgp_symm_alg_t cipher, + pgp_cipher_mode_t mode, + size_t tag_size, + bool disable_padding) +{ + EVP_CIPHER_CTX *ossl_ctx = + create(cipher, make_name(cipher, mode), false, tag_size, disable_padding); + if (!ossl_ctx) { + return NULL; + } + return std::unique_ptr<Cipher_OpenSSL>( + new (std::nothrow) Cipher_OpenSSL(cipher, ossl_ctx, tag_size, false)); +} + +bool +Cipher_OpenSSL::set_key(const uint8_t *key, size_t key_length) +{ + assert(key_length <= INT_MAX); + return EVP_CIPHER_CTX_set_key_length(m_ctx, (int) key_length) == 1 && + EVP_CipherInit_ex(m_ctx, NULL, NULL, key, NULL, -1) == 1; +} + +bool +Cipher_OpenSSL::set_iv(const uint8_t *iv, size_t iv_length) +{ + assert(iv_length <= INT_MAX); + // set IV len for AEAD modes + if (m_tag_size && + EVP_CIPHER_CTX_ctrl(m_ctx, EVP_CTRL_AEAD_SET_IVLEN, (int) iv_length, NULL) != 1) { + RNP_LOG("Failed to set AEAD IV length: %lu", ERR_peek_last_error()); + return false; + } + if (EVP_CIPHER_CTX_iv_length(m_ctx) != (int) iv_length) { + RNP_LOG("IV length mismatch"); + return false; + } + if (EVP_CipherInit_ex(m_ctx, NULL, NULL, NULL, iv, -1) != 1) { + RNP_LOG("Failed to set IV: %lu", ERR_peek_last_error()); + } + return true; +} + +bool +Cipher_OpenSSL::set_ad(const uint8_t *ad, size_t ad_length) +{ + assert(m_tag_size); + int outlen = 0; + if (EVP_CipherUpdate(m_ctx, NULL, &outlen, ad, ad_length) != 1) { + RNP_LOG("Failed to set AD: %lu", ERR_peek_last_error()); + return false; + } + return true; +} + +size_t +Cipher_OpenSSL::update_granularity() const +{ + return (size_t) EVP_CIPHER_CTX_block_size(m_ctx); +} + +bool +Cipher_OpenSSL::update(uint8_t * output, + size_t output_length, + size_t * output_written, + const uint8_t *input, + size_t input_length, + size_t * input_consumed) +{ + if (input_length > INT_MAX) { + return false; + } + *input_consumed = 0; + *output_written = 0; + if (input_length == 0) { + return true; + } + int outl = 0; + if (EVP_CipherUpdate(m_ctx, output, &outl, input, (int) input_length) != 1) { + RNP_LOG("EVP_CipherUpdate failed: %lu", ERR_peek_last_error()); + return false; + } + assert((size_t) outl < output_length); + *input_consumed = input_length; + *output_written = (size_t) outl; + return true; +} + +bool +Cipher_OpenSSL::finish(uint8_t * output, + size_t output_length, + size_t * output_written, + const uint8_t *input, + size_t input_length, + size_t * input_consumed) +{ + if (input_length > INT_MAX) { + return false; + } + if (!m_encrypt && input_length < m_tag_size) { + RNP_LOG("Insufficient input for final block (missing tag)"); + return false; + } + *input_consumed = 0; + *output_written = 0; + if (!m_encrypt && m_tag_size) { + // set the tag from the end of the ciphertext + if (EVP_CIPHER_CTX_ctrl(m_ctx, + EVP_CTRL_AEAD_SET_TAG, + m_tag_size, + const_cast<uint8_t *>(input) + input_length - m_tag_size) != + 1) { + RNP_LOG("Failed to set expected AEAD tag: %lu", ERR_peek_last_error()); + return false; + } + size_t ats = std::min(m_tag_size, input_length); + input_length -= ats; // m_tag_size; + *input_consumed += ats; // m_tag_size; + } + int outl = 0; + if (EVP_CipherUpdate(m_ctx, output, &outl, input, (int) input_length) != 1) { + RNP_LOG("EVP_CipherUpdate failed: %lu", ERR_peek_last_error()); + return false; + } + input += input_length; + *input_consumed += input_length; + output += outl; + *output_written += (size_t) outl; + if (EVP_CipherFinal_ex(m_ctx, output, &outl) != 1) { + RNP_LOG("EVP_CipherFinal_ex failed: %lu", ERR_peek_last_error()); + return false; + } + *output_written += (size_t) outl; + output += (size_t) outl; + if (m_encrypt && m_tag_size) { + // append the tag + if (EVP_CIPHER_CTX_ctrl(m_ctx, EVP_CTRL_AEAD_GET_TAG, m_tag_size, output) != 1) { + RNP_LOG("Failed to append AEAD tag: %lu", ERR_peek_last_error()); + return false; + } + *output_written += m_tag_size; + } + return true; +} + +Cipher_OpenSSL::Cipher_OpenSSL(pgp_symm_alg_t alg, + EVP_CIPHER_CTX *ctx, + size_t tag_size, + bool encrypt) + : Cipher(alg), m_ctx(ctx), m_tag_size(tag_size), m_encrypt(encrypt) +{ + m_block_size = EVP_CIPHER_CTX_block_size(m_ctx); +} + +Cipher_OpenSSL::~Cipher_OpenSSL() +{ + EVP_CIPHER_CTX_free(m_ctx); +} |