mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-24 02:47:11 +00:00
289 lines
7.7 KiB
C++
289 lines
7.7 KiB
C++
|
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
||
|
//
|
||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||
|
// purpose with or without fee is hereby granted, provided that the above
|
||
|
// copyright notice and this permission notice appear in all copies.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||
|
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||
|
|
||
|
#include <cryptolink.h>
|
||
|
#include <cryptolink/crypto_hmac.h>
|
||
|
|
||
|
#include <boost/scoped_ptr.hpp>
|
||
|
|
||
|
#include <openssl/hmac.h>
|
||
|
|
||
|
#include <cstring>
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
/// @brief Decode the HashAlgorithm enum into an EVP_MD pointer (or 0)
|
||
|
///
|
||
|
/// EVP_MD pointer is a OpenSSL's way of identifying hash algorithms
|
||
|
/// @param algorithm algorithm to be converted
|
||
|
/// @return pointer to EVP_MD which identifies the algorithm
|
||
|
const EVP_MD*
|
||
|
getOpenSSLHashAlgorithm(isc::cryptolink::HashAlgorithm algorithm) {
|
||
|
switch (algorithm) {
|
||
|
case isc::cryptolink::MD5:
|
||
|
return (EVP_md5());
|
||
|
case isc::cryptolink::SHA1:
|
||
|
return (EVP_sha1());
|
||
|
case isc::cryptolink::SHA256:
|
||
|
return (EVP_sha256());
|
||
|
case isc::cryptolink::SHA224:
|
||
|
return (EVP_sha224());
|
||
|
case isc::cryptolink::SHA384:
|
||
|
return (EVP_sha384());
|
||
|
case isc::cryptolink::SHA512:
|
||
|
return (EVP_sha512());
|
||
|
case isc::cryptolink::UNKNOWN_HASH:
|
||
|
return (0);
|
||
|
}
|
||
|
// compiler should have prevented us to reach this, since we have
|
||
|
// no default. But we need a return value anyway
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
/// Secure Buffers which are wiped out when released.
|
||
|
template<typename T>
|
||
|
struct SecBuf {
|
||
|
public:
|
||
|
typedef typename std::vector<T>::iterator iterator;
|
||
|
|
||
|
typedef typename std::vector<T>::const_iterator const_iterator;
|
||
|
|
||
|
explicit SecBuf() : vec_(std::vector<T>()) {}
|
||
|
|
||
|
explicit SecBuf(size_t n, const T& value = T()) :
|
||
|
vec_(std::vector<T>(n, value))
|
||
|
{}
|
||
|
|
||
|
SecBuf(iterator first, iterator last) :
|
||
|
vec_(std::vector<T>(first, last))
|
||
|
{}
|
||
|
|
||
|
SecBuf(const_iterator first, const_iterator last) :
|
||
|
vec_(std::vector<T>(first, last))
|
||
|
{}
|
||
|
|
||
|
SecBuf(const std::vector<T>& x) : vec_(x) {}
|
||
|
|
||
|
~SecBuf() {
|
||
|
std::memset(&vec_[0], 0, vec_.capacity() * sizeof(T));
|
||
|
};
|
||
|
|
||
|
iterator begin() {
|
||
|
return (vec_.begin());
|
||
|
};
|
||
|
|
||
|
const_iterator begin() const {
|
||
|
return (vec_.begin());
|
||
|
};
|
||
|
|
||
|
iterator end() {
|
||
|
return (vec_.end());
|
||
|
};
|
||
|
|
||
|
const_iterator end() const {
|
||
|
return (vec_.end());
|
||
|
};
|
||
|
|
||
|
size_t size() const {
|
||
|
return (vec_.size());
|
||
|
};
|
||
|
|
||
|
void resize(size_t sz) {
|
||
|
vec_.resize(sz);
|
||
|
};
|
||
|
|
||
|
SecBuf& operator=(const SecBuf& x) {
|
||
|
if (&x != *this) {
|
||
|
vec_ = x.vec_;
|
||
|
}
|
||
|
return (*this);
|
||
|
};
|
||
|
|
||
|
T& operator[](size_t n) {
|
||
|
return (vec_[n]);
|
||
|
};
|
||
|
|
||
|
const T& operator[](size_t n) const {
|
||
|
return (vec_[n]);
|
||
|
};
|
||
|
|
||
|
private:
|
||
|
std::vector<T> vec_;
|
||
|
};
|
||
|
|
||
|
} // local namespace
|
||
|
|
||
|
namespace isc {
|
||
|
namespace cryptolink {
|
||
|
|
||
|
/// @brief OpenSSL 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) {
|
||
|
const EVP_MD* algo = getOpenSSLHashAlgorithm(hash_algorithm);
|
||
|
if (algo == 0) {
|
||
|
isc_throw(UnsupportedAlgorithm,
|
||
|
"Unknown hash algorithm: " <<
|
||
|
static_cast<int>(hash_algorithm));
|
||
|
}
|
||
|
if (secret_len == 0) {
|
||
|
isc_throw(BadKey, "Bad HMAC secret length: 0");
|
||
|
}
|
||
|
|
||
|
md_.reset(new HMAC_CTX);
|
||
|
HMAC_CTX_init(md_.get());
|
||
|
|
||
|
HMAC_Init_ex(md_.get(), secret,
|
||
|
static_cast<int>(secret_len),
|
||
|
algo, NULL);
|
||
|
}
|
||
|
|
||
|
/// @brief Destructor
|
||
|
~HMACImpl() {
|
||
|
if (md_) {
|
||
|
HMAC_CTX_cleanup(md_.get());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// @brief Returns the output size of the digest
|
||
|
///
|
||
|
/// @return output size of the digest
|
||
|
size_t getOutputLength() const {
|
||
|
int size = HMAC_size(md_.get());
|
||
|
if (size < 0) {
|
||
|
isc_throw(isc::cryptolink::LibraryError, "EVP_MD_CTX_size");
|
||
|
}
|
||
|
return (static_cast<size_t>(size));
|
||
|
}
|
||
|
|
||
|
/// @brief Add data to digest
|
||
|
///
|
||
|
/// See @ref isc::cryptolink::HMAC::update() for details.
|
||
|
void update(const void* data, const size_t len) {
|
||
|
HMAC_Update(md_.get(), static_cast<const unsigned char*>(data), len);
|
||
|
}
|
||
|
|
||
|
/// @brief Calculate the final signature
|
||
|
///
|
||
|
/// See @ref isc::cryptolink::HMAC::sign() for details.
|
||
|
void sign(isc::util::OutputBuffer& result, size_t len) {
|
||
|
size_t size = getOutputLength();
|
||
|
SecBuf<unsigned char> digest(size);
|
||
|
HMAC_Final(md_.get(), &digest[0], NULL);
|
||
|
if (len == 0 || len > size) {
|
||
|
len = size;
|
||
|
}
|
||
|
result.writeData(&digest[0], len);
|
||
|
}
|
||
|
|
||
|
/// @brief Calculate the final signature
|
||
|
///
|
||
|
/// See @ref isc::cryptolink::HMAC::sign() for details.
|
||
|
void sign(void* result, size_t len) {
|
||
|
size_t size = getOutputLength();
|
||
|
SecBuf<unsigned char> digest(size);
|
||
|
HMAC_Final(md_.get(), &digest[0], NULL);
|
||
|
if (len > size) {
|
||
|
len = size;
|
||
|
}
|
||
|
std::memcpy(result, &digest[0], len);
|
||
|
}
|
||
|
|
||
|
/// @brief Calculate the final signature
|
||
|
///
|
||
|
/// See @ref isc::cryptolink::HMAC::sign() for details.
|
||
|
std::vector<uint8_t> sign(size_t len) {
|
||
|
size_t size = getOutputLength();
|
||
|
SecBuf<unsigned char> digest(size);
|
||
|
HMAC_Final(md_.get(), &digest[0], NULL);
|
||
|
if (len != 0 && len < size) {
|
||
|
digest.resize(len);
|
||
|
}
|
||
|
return (std::vector<uint8_t>(digest.begin(), digest.end()));
|
||
|
}
|
||
|
|
||
|
/// @brief Verify an existing signature
|
||
|
///
|
||
|
/// See @ref isc::cryptolink::HMAC::verify() for details.
|
||
|
bool verify(const void* sig, size_t len) {
|
||
|
size_t size = getOutputLength();
|
||
|
if (len != 0 && len < size / 2) {
|
||
|
return (false);
|
||
|
}
|
||
|
SecBuf<unsigned char> digest(size);
|
||
|
HMAC_Final(md_.get(), &digest[0], NULL);
|
||
|
if (len == 0 || len > size) {
|
||
|
len = size;
|
||
|
}
|
||
|
return (std::memcmp(&digest[0], sig, len) == 0);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
/// @brief The protected pointer to the OpenSSL HMAC_CTX structure
|
||
|
boost::scoped_ptr<HMAC_CTX> md_;
|
||
|
};
|
||
|
|
||
|
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_;
|
||
|
}
|
||
|
|
||
|
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
|