/* * 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 #include #include #include "cipher_botan.hpp" #include "utils.h" #include "types.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}, }; Cipher_Botan * Cipher_Botan::create(pgp_symm_alg_t alg, const std::string &name, bool encrypt) { #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 auto cipher = Botan::Cipher_Mode::create( name, encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION); if (!cipher) { RNP_LOG("Failed to create cipher '%s'", name.c_str()); return nullptr; } return new (std::nothrow) Cipher_Botan(alg, std::move(cipher)); } static std::string make_name(pgp_symm_alg_t cipher, pgp_cipher_mode_t mode, size_t tag_size, bool disable_padding) { 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 ""; } try { std::stringstream ss; ss << cipher_string << "/" << mode_string; if (tag_size) { ss << "(" << tag_size << ")"; } if (mode == PGP_CIPHER_MODE_CBC && disable_padding) { ss << "/NoPadding"; } return ss.str(); } catch (const std::exception &e) { return ""; } } std::unique_ptr Cipher_Botan::encryption(pgp_symm_alg_t cipher, pgp_cipher_mode_t mode, size_t tag_size, bool disable_padding) { return std::unique_ptr( create(cipher, make_name(cipher, mode, tag_size, disable_padding), true)); } std::unique_ptr Cipher_Botan::decryption(pgp_symm_alg_t cipher, pgp_cipher_mode_t mode, size_t tag_size, bool disable_padding) { return std::unique_ptr( create(cipher, make_name(cipher, mode, tag_size, disable_padding), false)); } size_t Cipher_Botan::update_granularity() const { return m_cipher->update_granularity(); } bool Cipher_Botan::set_key(const uint8_t *key, size_t key_length) { try { m_cipher->set_key(key, key_length); } catch (const std::exception &e) { RNP_LOG("Failed to set key: %s", e.what()); return false; } return true; } bool Cipher_Botan::set_iv(const uint8_t *iv, size_t iv_length) { try { m_cipher->start(iv, iv_length); m_buf.reserve(this->update_granularity()); } catch (const std::exception &e) { RNP_LOG("Failed to set IV: %s", e.what()); return false; } return true; } bool Cipher_Botan::set_ad(const uint8_t *ad, size_t ad_length) { assert(m_cipher->authenticated()); try { dynamic_cast(*m_cipher).set_associated_data(ad, ad_length); } catch (const std::exception &e) { RNP_LOG("Failed to set AAD: %s", e.what()); return false; } return true; } bool Cipher_Botan::update(uint8_t * output, size_t output_length, size_t * output_written, const uint8_t *input, size_t input_length, size_t * input_consumed) { try { size_t ud = this->update_granularity(); m_buf.resize(ud); *input_consumed = 0; *output_written = 0; while (input_length >= ud && output_length >= ud) { m_buf.assign(input, input + ud); size_t written = m_cipher->process(m_buf.data(), ud); std::copy(m_buf.data(), m_buf.data() + written, output); input += ud; output += written; input_length -= ud; output_length -= written; *output_written += written; *input_consumed += ud; } } catch (const std::exception &e) { RNP_LOG("%s", e.what()); return false; } return true; } bool Cipher_Botan::finish(uint8_t * output, size_t output_length, size_t * output_written, const uint8_t *input, size_t input_length, size_t * input_consumed) { try { *input_consumed = 0; *output_written = 0; size_t ud = this->update_granularity(); if (input_length > ud) { if (!update(output, output_length, output_written, input, input_length - ud, input_consumed)) { return false; } input += *input_consumed; input_length = input_length - *input_consumed; output += *output_written; output_length -= *output_written; } Botan::secure_vector final_block(input, input + input_length); m_cipher->finish(final_block); if (final_block.size() > output_length) { RNP_LOG("Insufficient buffer"); return false; } std::copy(final_block.begin(), final_block.end(), output); *output_written += final_block.size(); *input_consumed += input_length; } catch (const std::exception &e) { RNP_LOG("%s", e.what()); return false; } return true; } Cipher_Botan::Cipher_Botan(pgp_symm_alg_t alg, std::unique_ptr cipher) : Cipher(alg), m_cipher(std::move(cipher)) { } Cipher_Botan::~Cipher_Botan() { }