Browse Source

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

Jelte Jansen 14 years ago
parent
commit
e74f6d5e8f

+ 1 - 0
src/lib/crypto/Makefile.am

@@ -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

+ 23 - 149
src/lib/crypto/crypto.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

+ 16 - 99
src/lib/crypto/crypto.h

@@ -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
 ///

+ 168 - 0
src/lib/crypto/crypto_hmac.cc

@@ -0,0 +1,168 @@
+
+#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

+ 128 - 0
src/lib/crypto/crypto_hmac.h

@@ -0,0 +1,128 @@
+// 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
+    ///
+    /// \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_;
+};
+
+} // namespace crypto
+} // namespace isc
+
+#endif // __ISC_CRYPTO_HMAC
+

+ 6 - 0
src/lib/crypto/tests/crypto_unittests.cc

@@ -484,3 +484,9 @@ TEST(CryptoTest, BadKey) {
                             hmac_sig.getLength()),
                             UnsupportedAlgorithm);
 }
+
+TEST(CryptoTest, Singleton) {
+    Crypto& c1 = Crypto::getCrypto();
+    Crypto& c2 = Crypto::getCrypto();
+    ASSERT_EQ(&c1, &c2);
+}

+ 2 - 2
src/lib/crypto/tests/run_unittests.cc

@@ -20,7 +20,7 @@
 int
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
-    isc::crypto::Crypto crypto;
 
-    return (RUN_ALL_TESTS());
+    int result = RUN_ALL_TESTS();
+    return (result);
 }