Commit e74f6d5e authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[trac781] make a singleton entry point, moved hmac into own source file

parent f137e0fa
......@@ -9,3 +9,4 @@ CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libb10crypto.la
libb10crypto_la_SOURCES = crypto.h crypto.cc
libb10crypto_la_SOURCES += crypto_hmac.h crypto_hmac.cc
......@@ -27,32 +27,11 @@
#include <boost/scoped_ptr.hpp>
#include <iostream>
using namespace std;
using namespace isc::dns;
namespace {
const char*
getBotanHashAlgorithmName(isc::crypto::HMAC::HashAlgorithm algorithm) {
switch (algorithm) {
case isc::crypto::HMAC::MD5:
return ("MD5");
break;
case isc::crypto::HMAC::SHA1:
return ("SHA-1");
break;
case isc::crypto::HMAC::SHA256:
return ("SHA-256");
break;
case isc::crypto::HMAC::UNKNOWN:
return ("Unknown");
break;
}
// compiler should have prevented us to reach this, since we have
// no default. But we need a return value anyway
return ("Unknown");
}
} // local namespace
namespace isc {
namespace crypto {
......@@ -67,144 +46,39 @@ private:
Botan::LibraryInitializer _botan_init;
};
Crypto::Crypto() {
try {
impl_ = new CryptoImpl();
} catch (const Botan::Exception& ex) {
isc_throw(InitializationError, ex.what());
}
}
Crypto::~Crypto() {
delete impl_;
}
class HMACImpl {
public:
explicit HMACImpl(const void* secret, size_t secret_len,
const HMAC::HashAlgorithm hash_algorithm) {
Botan::HashFunction* hash;
try {
hash = Botan::get_hash(
getBotanHashAlgorithmName(hash_algorithm));
} catch (const Botan::Algorithm_Not_Found&) {
isc_throw(isc::crypto::UnsupportedAlgorithm,
"Unknown hash algorithm: " + hash_algorithm);
}
hmac_.reset(new Botan::HMAC::HMAC(hash));
// If the key length is larger than the block size, we hash the
// key itself first.
try {
if (secret_len > hash->HASH_BLOCK_SIZE) {
Botan::SecureVector<Botan::byte> hashed_key =
hash->process(static_cast<const Botan::byte*>(secret),
secret_len);
hmac_->set_key(hashed_key.begin(), hashed_key.size());
} else {
hmac_->set_key(static_cast<const Botan::byte*>(secret),
secret_len);
}
} catch (const Botan::Invalid_Key_Length& ikl) {
isc_throw(BadKey, ikl.what());
}
}
~HMACImpl() { }
size_t getOutputLength() const {
return (hmac_->OUTPUT_LENGTH);
}
void update(const void* data, const size_t len) {
hmac_->update(static_cast<const Botan::byte*>(data), len);
}
void sign(isc::dns::OutputBuffer& result, size_t len) {
Botan::SecureVector<Botan::byte> b_result(hmac_->final());
if (len == 0 || len > b_result.size()) {
len = b_result.size();
}
result.writeData(b_result.begin(), len);
}
void sign(void* result, size_t len) {
Botan::SecureVector<Botan::byte> b_result(hmac_->final());
size_t output_size = getOutputLength();
if (output_size > len) {
output_size = len;
}
memcpy(result, b_result.begin(), output_size);
}
std::vector<uint8_t> sign(size_t len) {
Botan::SecureVector<Botan::byte> b_result(hmac_->final());
if (len == 0 || len > b_result.size()) {
return (std::vector<uint8_t>(b_result.begin(), b_result.end()));
} else {
return (std::vector<uint8_t>(b_result.begin(), &b_result[len]));
}
Crypto&
Crypto::getCrypto() {
Crypto &c = getCryptoInternal();
if (!c.impl_) {
c.initialize();
}
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
Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
if (len == 0 || len > getOutputLength()) {
len = getOutputLength();
}
return (Botan::same_mem(&our_mac[0],
static_cast<const unsigned char*>(sig),
len));
}
private:
boost::scoped_ptr<Botan::HMAC> hmac_;
};
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);
return c;
}
void
HMAC::sign(isc::dns::OutputBuffer& result, size_t len) {
impl_->sign(result, len);
Crypto&
Crypto::getCryptoInternal() {
static Crypto instance;
return (instance);
}
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);
Crypto::initialize() {
Crypto& c = getCryptoInternal();
try {
c.impl_ = new CryptoImpl();
} catch (const Botan::Exception& ex) {
isc_throw(InitializationError, ex.what());
}
}
bool
HMAC::verify(const void* sig, const size_t len) {
return (impl_->verify(sig, len));
HMAC*
Crypto::createHMAC(const void* secret, size_t secret_len,
const HMAC::HashAlgorithm hash_algorithm) {
return new HMAC(secret, secret_len, hash_algorithm);
}
void
......
......@@ -18,6 +18,8 @@
#include <boost/noncopyable.hpp>
#include <crypto/crypto_hmac.h>
#ifndef _ISC_CRYPTO_H
#define _ISC_CRYPTO_H
......@@ -67,110 +69,25 @@ class CryptoImpl;
/// Preferably, this object is created in the program's main() function
// Internal note: we can use this class later to initialize and manage
// dynamic (PKCS#11) libs
class Crypto {
class Crypto : private boost::noncopyable {
public:
Crypto();
~Crypto();
static Crypto& getCrypto();
static void initialize();
bool initialized() { return (impl_ != NULL); }
HMAC* createHMAC(const void* secret, size_t secret_len,
const HMAC::HashAlgorithm hash_algorithm);
private:
static Crypto& getCryptoInternal();
Crypto() : impl_(NULL) {};
~Crypto();
CryptoImpl* impl_;
};
/// Forward declaration, pimpl style
class HMACImpl;
/// \brief HMAC support
///
/// This class is used to create and verify HMAC signatures
///
class HMAC : private boost::noncopyable {
public:
enum HashAlgorithm {
MD5 = 0, ///< MD5
SHA1 = 1, ///< SHA-1
SHA256 = 2, ///< SHA-256
UNKNOWN = 3 ///< This value can be used in conversion
/// functions, to be returned when the
/// input is unknown (but a value MUST be
/// returned), for instance when the input
/// is a Name or a string, and the return
/// value is a HashAlgorithm.
};
/// \brief Constructor from a secret and a hash algorithm
///
/// \exception UnsupportedAlgorithmException if the given algorithm
/// is unknown or not supported by the underlying library
/// \exception InvalidKeyLength if the given key secret_len is bad
///
/// Notes: if the secret is longer than the block size of its
/// algorithm, the constructor will run it through the hash
/// algorithm, and use the digest as the secret for this HMAC
/// operation
///
/// \param secret The secret to sign with
/// \param len The length of the secret
/// \param hash_algorithm The hash algorithm
explicit HMAC(const void* secret, size_t secret_len,
const HashAlgorithm hash_algorithm);
/// \brief Destructor
~HMAC();
/// \brief Returns the output size of the digest
///
/// \return output size of the digest
size_t getOutputLength() const;
/// \brief Add data to digest
///
/// \param data The data to add
/// \param len The size of the data
void update(const void* data, const size_t len);
/// \brief Calculate the final signature
///
/// The result will be appended to the given outputbuffer
///
/// \param result The OutputBuffer to append the result to
/// \param len The number of bytes from the result to copy. If this
/// value is smaller than the algorithms output size, the
/// result will be truncated. If this value is larger, or 0
/// (the default), it will be ignored
void sign(isc::dns::OutputBuffer& result, size_t len = 0);
/// \brief Calculate the final signature
///
/// len bytes of data from the result will be copied to *result
/// If len is larger than the output size, only output_size bytes
/// will be copied. If it is smaller, the output will be truncated
///
/// At least len bytes of data must be available for writing at
/// result
void sign(void* result, size_t len);
/// \brief Calculate the final signatre
///
/// The result will be returned as a std::vector<uint8_t>
///
/// \param len The number of bytes from the result to copy. If this
/// value is smaller than the algorithms output size, the
/// result will be truncated. If this value is larger, or 0
/// (the default), it will be ignored
/// \return a vector containing the signature
std::vector<uint8_t> sign(size_t len = 0);
/// \brief Verify an existing signature
///
/// \param sig The signature to verify
/// \param len The length of the signature. If this is non-zero,
/// and smaller than the output length of the algorithm,
/// only len bytes will be checked
/// \return true if the signature is correct, false otherwise
bool verify(const void* sig, size_t len);
private:
HMACImpl* impl_;
};
/// Entry point for the API
/// If the library has not been initialized, this will automatically
/// initialize it with default values
/// \brief Create an HMAC signature for the given data
///
......
#include <crypto.h>
#include <crypto/crypto_hmac.h>
#include <boost/scoped_ptr.hpp>
#include <botan/botan.h>
#include <botan/hmac.h>
#include <botan/hash.h>
#include <botan/types.h>
namespace {
const char*
getBotanHashAlgorithmName(isc::crypto::HMAC::HashAlgorithm algorithm) {
switch (algorithm) {
case isc::crypto::HMAC::MD5:
return ("MD5");
break;
case isc::crypto::HMAC::SHA1:
return ("SHA-1");
break;
case isc::crypto::HMAC::SHA256:
return ("SHA-256");
break;
case isc::crypto::HMAC::UNKNOWN:
return ("Unknown");
break;
}
// compiler should have prevented us to reach this, since we have
// no default. But we need a return value anyway
return ("Unknown");
}
} // local namespace
namespace isc {
namespace crypto {
class HMACImpl {
public:
explicit HMACImpl(const void* secret, size_t secret_len,
const HMAC::HashAlgorithm hash_algorithm) {
Botan::HashFunction* hash;
try {
hash = Botan::get_hash(
getBotanHashAlgorithmName(hash_algorithm));
} catch (const Botan::Algorithm_Not_Found&) {
isc_throw(isc::crypto::UnsupportedAlgorithm,
"Unknown hash algorithm: " + hash_algorithm);
}
hmac_.reset(new Botan::HMAC::HMAC(hash));
// If the key length is larger than the block size, we hash the
// key itself first.
try {
if (secret_len > hash->HASH_BLOCK_SIZE) {
Botan::SecureVector<Botan::byte> hashed_key =
hash->process(static_cast<const Botan::byte*>(secret),
secret_len);
hmac_->set_key(hashed_key.begin(), hashed_key.size());
} else {
hmac_->set_key(static_cast<const Botan::byte*>(secret),
secret_len);
}
} catch (const Botan::Invalid_Key_Length& ikl) {
isc_throw(BadKey, ikl.what());
}
}
~HMACImpl() { }
size_t getOutputLength() const {
return (hmac_->OUTPUT_LENGTH);
}
void update(const void* data, const size_t len) {
hmac_->update(static_cast<const Botan::byte*>(data), len);
}
void sign(isc::dns::OutputBuffer& result, size_t len) {
Botan::SecureVector<Botan::byte> b_result(hmac_->final());
if (len == 0 || len > b_result.size()) {
len = b_result.size();
}
result.writeData(b_result.begin(), len);
}
void sign(void* result, size_t len) {
Botan::SecureVector<Botan::byte> b_result(hmac_->final());
size_t output_size = getOutputLength();
if (output_size > len) {
output_size = len;
}
memcpy(result, b_result.begin(), output_size);
}
std::vector<uint8_t> sign(size_t len) {
Botan::SecureVector<Botan::byte> b_result(hmac_->final());
if (len == 0 || len > b_result.size()) {
return (std::vector<uint8_t>(b_result.begin(), b_result.end()));
} else {
return (std::vector<uint8_t>(b_result.begin(), &b_result[len]));
}
}
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
Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
if (len == 0 || len > getOutputLength()) {
len = getOutputLength();
}
return (Botan::same_mem(&our_mac[0],
static_cast<const unsigned char*>(sig),
len));
}
private:
boost::scoped_ptr<Botan::HMAC> hmac_;
};
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::dns::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 crypto
} // namespace isc
// Copyright (C) 2011 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 <string>
#include <dns/buffer.h>
#include <exceptions/exceptions.h>
#include <boost/noncopyable.hpp>
#ifndef _ISC_CRYPTO_HMAC_H
#define _ISC_CRYPTO_HMAC_H
namespace isc {
namespace crypto {
/// Forward declaration, pimpl style
class HMACImpl;
/// \brief HMAC support
///
/// This class is used to create and verify HMAC signatures
///
class HMAC : private boost::noncopyable {
public:
enum HashAlgorithm {
MD5 = 0, ///< MD5
SHA1 = 1, ///< SHA-1
SHA256 = 2, ///< SHA-256
UNKNOWN = 3 ///< This value can be used in conversion
/// functions, to be returned when the
/// input is unknown (but a value MUST be
/// returned), for instance when the input
/// is a Name or a string, and the return
/// value is a HashAlgorithm.
};
/// \brief Constructor from a secret and a hash algorithm
///
/// \exception UnsupportedAlgorithmException if the given algorithm
/// is unknown or not supported by the underlying library
/// \exception InvalidKeyLength if the given key secret_len is bad
///
/// Notes: if the secret is longer than the block size of its
/// algorithm, the constructor will run it through the hash
/// algorithm, and use the digest as the secret for this HMAC
/// operation
///
/// \param secret The secret to sign with
/// \param len The length of the secret
/// \param hash_algorithm The hash algorithm
explicit HMAC(const void* secret, size_t secret_len,
const HashAlgorithm hash_algorithm);
/// \brief Destructor
~HMAC();
/// \brief Returns the output size of the digest
///
/// \return output size of the digest
size_t getOutputLength() const;
/// \brief Add data to digest
///
/// \param data The data to add
/// \param len The size of the data
void update(const void* data, const size_t len);
/// \brief Calculate the final signature
///
/// The result will be appended to the given outputbuffer
///
/// \param result The OutputBuffer to append the result to
/// \param len The number of bytes from the result to copy. If this
/// value is smaller than the algorithms output size, the
/// result will be truncated. If this value is larger, or 0
/// (the default), it will be ignored
void sign(isc::dns::OutputBuffer& result, size_t len = 0);
/// \brief Calculate the final signature
///
/// len bytes of data from the result will be copied to *result
/// If len is larger than the output size, only output_size bytes
/// will be copied. If it is smaller, the output will be truncated
///
/// At least len bytes of data must be available for writing at
/// result
void sign(void* result, size_t len);
/// \brief Calculate the final signatre
///
/// The result will be returned as a std::vector<uint8_t>
///
/// \param len The number of bytes from the result to copy. If this
/// value is smaller than the algorithms output size, the
/// result will be truncated. If this value is larger, or 0
/// (the default), it will be ignored
/// \return a vector containing the signature
std::vector<uint8_t> sign(size_t len = 0);
/// \brief Verify an existing signature