diff options
Diffstat (limited to 'src/lib/cryptolink/botan_hmac.cc')
-rw-r--r-- | src/lib/cryptolink/botan_hmac.cc | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/src/lib/cryptolink/botan_hmac.cc b/src/lib/cryptolink/botan_hmac.cc new file mode 100644 index 0000000..88efb2e --- /dev/null +++ b/src/lib/cryptolink/botan_hmac.cc @@ -0,0 +1,245 @@ +// Copyright (C) 2011-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cryptolink.h> +#include <cryptolink/crypto_hmac.h> + +#include <boost/scoped_ptr.hpp> + +#include <botan/hmac.h> +#include <botan/lookup.h> + +#include <cryptolink/botan_common.h> + +namespace isc { +namespace cryptolink { + +/// @brief Botan implementation of HMAC. Each method is the counterpart +/// of the HMAC corresponding method. +class HMACImpl { +public: + /// @brief Constructor from a secret and a hash algorithm + /// + /// See constructor of the @ref isc::cryptolink::HMAC class for details. + /// + /// @param secret The secret to sign with + /// @param secret_len The length of the secret + /// @param hash_algorithm The hash algorithm + explicit HMACImpl(const void* secret, size_t secret_len, + const HashAlgorithm hash_algorithm) + : hash_algorithm_(hash_algorithm), hmac_() { + Botan::HashFunction* hash; + try { + const std::string& name = + btn::getHashAlgorithmName(hash_algorithm); + std::unique_ptr<Botan::HashFunction> hash_ptr = + Botan::HashFunction::create(name); + if (hash_ptr) { + hash = hash_ptr.release(); + } else { + throw Botan::Algorithm_Not_Found(name); + } + } catch (const Botan::Algorithm_Not_Found&) { + isc_throw(UnsupportedAlgorithm, + "Unknown hash algorithm: " << + static_cast<int>(hash_algorithm)); + } catch (const Botan::Exception& exc) { + isc_throw(LibraryError, "Botan error: " << exc.what()); + } + + hmac_.reset(new Botan::HMAC(hash)); + + // If the key length is larger than the block size, we hash the + // key itself first. + try { + // use a temp var so we don't have blocks spanning + // preprocessor directives + size_t block_length = hash->hash_block_size(); + if (secret_len > block_length) { + Botan::secure_vector<Botan::byte> hashed_key = + hash->process(static_cast<const Botan::byte*>(secret), + secret_len); + hmac_->set_key(&hashed_key[0], hashed_key.size()); + } else { + // Botan 1.8 considers len 0 a bad key. 1.9 does not, + // but we won't accept it anyway, and fail early + if (secret_len == 0) { + isc_throw(BadKey, "Bad HMAC secret length: 0"); + } + hmac_->set_key(static_cast<const Botan::byte*>(secret), + secret_len); + } + } catch (const Botan::Invalid_Key_Length& ikl) { + isc_throw(BadKey, ikl.what()); + } catch (const Botan::Exception& exc) { + isc_throw(LibraryError, "Botan error: " << exc.what()); + } + } + + /// @brief Destructor + ~HMACImpl() { + } + + /// @brief Returns the HashAlgorithm of the object + HashAlgorithm getHashAlgorithm() const { + return (hash_algorithm_); + } + + /// @brief Returns the output size of the digest + /// + /// @return output size of the digest + size_t getOutputLength() const { + return (hmac_->output_length()); + } + + /// @brief Add data to digest + /// + /// See @ref isc::cryptolink::HMAC::update() for details. + void update(const void* data, const size_t len) { + try { + hmac_->update(static_cast<const Botan::byte*>(data), len); + } catch (const Botan::Exception& exc) { + isc_throw(LibraryError, "Botan error: " << exc.what()); + } + } + + /// @brief Calculate the final signature + /// + /// See @ref isc::cryptolink::HMAC::sign() for details. + void sign(isc::util::OutputBuffer& result, size_t len) { + try { + Botan::secure_vector<Botan::byte> b_result(hmac_->final()); + + if (len > b_result.size()) { + len = b_result.size(); + } + result.writeData(&b_result[0], len); + } catch (const Botan::Exception& exc) { + isc_throw(LibraryError, "Botan error: " << exc.what()); + } + } + + /// @brief Calculate the final signature + /// + /// See @ref isc::cryptolink::HMAC::sign() for details. + void sign(void* result, size_t len) { + try { + Botan::secure_vector<Botan::byte> b_result(hmac_->final()); + size_t output_size = getOutputLength(); + if (output_size > len) { + output_size = len; + } + std::memcpy(result, &b_result[0], output_size); + } catch (const Botan::Exception& exc) { + isc_throw(LibraryError, "Botan error: " << exc.what()); + } + } + + /// @brief Calculate the final signature + /// + /// See @ref isc::cryptolink::HMAC::sign() for details. + std::vector<uint8_t> sign(size_t len) { + try { + Botan::secure_vector<Botan::byte> b_result(hmac_->final()); + if (len > b_result.size()) { + len = b_result.size(); + } + // Return vector with content. Construct &b_result[len] attempts + // to get an address of one element beyond the b_result. Replaced + // with the address of first element + len + return (std::vector<uint8_t>(&b_result[0], &b_result[0]+len)); + } catch (const Botan::Exception& exc) { + isc_throw(LibraryError, "Botan error: " << exc.what()); + } + } + + + /// @brief Verify an existing signature + /// + /// See @ref isc::cryptolink::HMAC::verify() for details. + bool verify(const void* sig, size_t len) { + // Botan's verify_mac checks if len matches the output_length, + // which causes it to fail for truncated signatures, so we do + // the check ourselves + try { + size_t size = getOutputLength(); + if (len < 10 || len < size / 2) { + return (false); + } + if (len > size) { + len = size; + } + if (digest_.size() == 0) { + digest_ = hmac_->final(); + } + return (Botan::same_mem(&digest_[0], + static_cast<const unsigned char*>(sig), + len)); + } catch (const Botan::Exception& exc) { + isc_throw(LibraryError, "Botan error: " << exc.what()); + } + } + +private: + /// @brief The hash algorithm + HashAlgorithm hash_algorithm_; + + /// @brief The protected pointer to the Botan HMAC object + boost::scoped_ptr<Botan::HMAC> hmac_; + + /// @brief The digest cache for multiple verify + Botan::secure_vector<Botan::byte> digest_; +}; + +HMAC::HMAC(const void* secret, size_t secret_length, + const HashAlgorithm hash_algorithm) +{ + impl_ = new HMACImpl(secret, secret_length, hash_algorithm); +} + +HMAC::~HMAC() { + delete impl_; +} + +HashAlgorithm +HMAC::getHashAlgorithm() const { + return (impl_->getHashAlgorithm()); +} + +size_t +HMAC::getOutputLength() const { + return (impl_->getOutputLength()); +} + +void +HMAC::update(const void* data, const size_t len) { + impl_->update(data, len); +} + +void +HMAC::sign(isc::util::OutputBuffer& result, size_t len) { + impl_->sign(result, len); +} + +void +HMAC::sign(void* result, size_t len) { + impl_->sign(result, len); +} + +std::vector<uint8_t> +HMAC::sign(size_t len) { + return impl_->sign(len); +} + +bool +HMAC::verify(const void* sig, const size_t len) { + return (impl_->verify(sig, len)); +} + +} // namespace cryptolink +} // namespace isc |