Browse Source

[master] Merge branch 'trac781'

Conflicts:
	configure.ac
	doc/Doxyfile
	src/lib/Makefile.am

Also updated includes (because of the lib/util move)
Jelte Jansen 14 years ago
parent
commit
9df42279a4

+ 39 - 5
configure.ac

@@ -374,6 +374,38 @@ if test "$lcov" != "no"; then
 fi
 AC_SUBST(USE_LCOV)
 
+# Check for Botan
+botan_path=""
+AC_ARG_WITH([botan],
+  AC_HELP_STRING([--with-botan=PATH],
+    [specify exact directory of Botan library]),
+    [botan_path="$withval"])
+# If not specificed, try some common paths
+if test -z "$with_botan"; then
+    botandirs="/usr/local /usr/pkg /opt /opt/local /usr"
+    for d in $botandirs
+    do
+        if test -f $d/include/botan/botan.h; then
+            botan_path=$d
+            break
+        fi
+    done
+fi
+if test "${botan_path}" ; then
+    CPPFLAGS="$CPPFLAGS -I${botan_path}/include"
+    LDFLAGS="$LDFLAGS -L${botan_path}/lib -lbotan"
+fi
+AC_CHECK_HEADERS([botan/botan.h],,AC_MSG_ERROR([Missing required header files.]))
+AC_LINK_IFELSE(
+        [AC_LANG_PROGRAM([#include <botan/botan.h>],
+                         [using namespace Botan;
+                          LibraryInitializer::initialize();
+                         ])],
+        [AC_MSG_RESULT([checking for Botan library... yes])],
+        [AC_MSG_RESULT([checking for Botan library... no])
+         AC_MSG_ERROR([Missing Botan library])]
+)
+
 #
 # Configure Boost header path
 #
@@ -673,11 +705,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/config/Makefile
                  src/lib/config/tests/Makefile
                  src/lib/config/tests/testdata/Makefile
-                 src/lib/util/Makefile
-                 src/lib/util/io/Makefile
-                 src/lib/util/io/tests/Makefile
-                 src/lib/util/unittests/Makefile
-                 src/lib/util/tests/Makefile
+                 src/lib/cryptolink/Makefile
+                 src/lib/cryptolink/tests/Makefile
                  src/lib/dns/Makefile
                  src/lib/dns/tests/Makefile
                  src/lib/dns/tests/testdata/Makefile
@@ -702,6 +731,11 @@ AC_CONFIG_FILES([Makefile
                  src/lib/cache/tests/Makefile
                  src/lib/server_common/Makefile
                  src/lib/server_common/tests/Makefile
+                 src/lib/util/Makefile
+                 src/lib/util/io/Makefile
+                 src/lib/util/io/tests/Makefile
+                 src/lib/util/unittests/Makefile
+                 src/lib/util/tests/Makefile
                  tests/Makefile
                  tests/system/Makefile
                  tests/tools/Makefile

+ 6 - 5
doc/Doxyfile

@@ -568,11 +568,12 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns \
-    ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth \
-    ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ \
-    ../src/lib/nsas ../src/lib/testutils ../src/lib/cache \
-    ../src/lib/server_common/ ../src/bin/sockcreator/ ../src/lib/util/
+INPUT                  = ../src/lib/cc ../src/lib/config \
+    ../src/lib/cryptolink ../src/lib/dns ../src/lib/datasrc \
+    ../src/bin/auth ../src/bin/resolver ../src/lib/bench \
+    ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas \
+    ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
+    ../src/bin/sockcreator/ ../src/lib/util/
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

+ 3 - 2
src/lib/Makefile.am

@@ -1,2 +1,3 @@
-SUBDIRS = exceptions util dns cc config python xfr bench log asiolink \
-          asiodns nsas cache resolve testutils datasrc server_common
+SUBDIRS = exceptions util cryptolink dns cc config python xfr bench \
+          log asiolink asiodns nsas cache resolve testutils datasrc \
+          server_common

+ 12 - 0
src/lib/cryptolink/Makefile.am

@@ -0,0 +1,12 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+CLEANFILES = *.gcno *.gcda
+
+lib_LTLIBRARIES = libcryptolink.la
+
+libcryptolink_la_SOURCES = cryptolink.h cryptolink.cc
+libcryptolink_la_SOURCES += crypto_hmac.h crypto_hmac.cc

+ 237 - 0
src/lib/cryptolink/crypto_hmac.cc

@@ -0,0 +1,237 @@
+// 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 <cryptolink.h>
+#include <cryptolink/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::cryptolink::HashAlgorithm algorithm) {
+    switch (algorithm) {
+    case isc::cryptolink::MD5:
+        return ("MD5");
+        break;
+    case isc::cryptolink::SHA1:
+        return ("SHA-1");
+        break;
+    case isc::cryptolink::SHA256:
+        return ("SHA-256");
+        break;
+    case isc::cryptolink::UNKNOWN_HASH:
+        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 cryptolink {
+
+class HMACImpl {
+public:
+    explicit HMACImpl(const void* secret, size_t secret_len,
+                      const HashAlgorithm hash_algorithm) {
+        Botan::HashFunction* hash;
+        try {
+            hash = Botan::get_hash(
+                getBotanHashAlgorithmName(hash_algorithm));
+        } catch (const Botan::Algorithm_Not_Found&) {
+            isc_throw(isc::cryptolink::UnsupportedAlgorithm,
+                      "Unknown hash algorithm: " + hash_algorithm);
+        } catch (const Botan::Exception& exc) {
+            isc_throw(isc::cryptolink::LibraryError, exc.what());
+        }
+
+        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());
+        } catch (const Botan::Exception& exc) {
+            isc_throw(isc::cryptolink::LibraryError, exc.what());
+        }
+    }
+
+    ~HMACImpl() { }
+
+    size_t getOutputLength() const {
+        return (hmac_->OUTPUT_LENGTH);
+    }
+
+    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(isc::cryptolink::LibraryError, exc.what());
+        }
+    }
+
+    void sign(isc::util::OutputBuffer& result, size_t len) {
+        try {
+            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);
+        } catch (const Botan::Exception& exc) {
+            isc_throw(isc::cryptolink::LibraryError, exc.what());
+        }
+    }
+
+    void sign(void* result, size_t len) {
+        try {
+            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);
+        } catch (const Botan::Exception& exc) {
+            isc_throw(isc::cryptolink::LibraryError, exc.what());
+        }
+    }
+
+    std::vector<uint8_t> sign(size_t len) {
+        try {
+            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]));
+            }
+        } catch (const Botan::Exception& exc) {
+            isc_throw(isc::cryptolink::LibraryError, exc.what());
+        }
+    }
+
+
+    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 {
+            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));
+        } catch (const Botan::Exception& exc) {
+            isc_throw(isc::cryptolink::LibraryError, exc.what());
+        }
+    }
+
+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::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));
+}
+
+void
+signHMAC(const void* data, size_t data_len, const void* secret,
+         size_t secret_len, const HashAlgorithm hash_algorithm,
+         isc::util::OutputBuffer& result, size_t len)
+{
+    boost::scoped_ptr<HMAC> hmac(
+        CryptoLink::getCryptoLink().createHMAC(secret,
+                                               secret_len,
+                                               hash_algorithm));
+    hmac->update(data, data_len);
+    hmac->sign(result, len);
+}
+
+
+bool
+verifyHMAC(const void* data, const size_t data_len, const void* secret,
+           size_t secret_len, const HashAlgorithm hash_algorithm,
+           const void* sig, const size_t sig_len)
+{
+    boost::scoped_ptr<HMAC> hmac(
+        CryptoLink::getCryptoLink().createHMAC(secret,
+                                               secret_len,
+                                               hash_algorithm));
+    hmac->update(data, data_len);
+    return (hmac->verify(sig, sig_len));
+}
+
+void
+deleteHMAC(HMAC* hmac) {
+    delete hmac;
+}
+
+} // namespace cryptolink
+} // namespace isc

+ 209 - 0
src/lib/cryptolink/crypto_hmac.h

@@ -0,0 +1,209 @@
+// 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 <util/buffer.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <cryptolink/cryptolink.h>
+
+#ifndef _ISC_CRYPTO_HMAC_H
+#define _ISC_CRYPTO_HMAC_H
+
+namespace isc {
+namespace cryptolink {
+
+/// Forward declaration, pimpl style
+class HMACImpl;
+
+/// \brief HMAC support
+///
+/// This class is used to create and verify HMAC signatures. Instances
+/// can be created with CryptoLink::createHMAC()
+///
+class HMAC : private boost::noncopyable {
+private:
+    /// \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
+    /// \exception LibraryError if there was any unexpected exception
+    ///                         in the underlying library
+    ///
+    /// 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
+    HMAC(const void* secret, size_t secret_len,
+         const HashAlgorithm hash_algorithm);
+
+    friend HMAC* CryptoLink::createHMAC(const void*, size_t,
+                                        const HashAlgorithm);
+
+public:
+    /// \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
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///                         in the underlying library
+    ///
+    /// \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
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///                         in the underlying library
+    ///
+    /// \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::util::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
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///                         in the underlying library
+    ///
+    /// 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>
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///                         in the underlying library
+    ///
+    /// \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
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///                         in the underlying library
+    ///
+    /// \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_;
+};
+
+/// \brief Create an HMAC signature for the given data
+///
+/// This is a convenience function that calculates the hmac signature,
+/// given a fixed amount of data. Internally it does the same as
+/// creating an HMAC object, feeding it the data, and calculating the
+/// resulting signature.
+///
+/// \exception UnsupportedAlgorithm if the given algorithm is unknown
+///            or not supported by the underlying library
+/// \exception BadKey if the given key secret_len is bad
+/// \exception LibraryError if there was any unexpected exception
+///                         in the underlying library
+///
+/// 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 data The data to sign
+/// \param data_len The length of the data
+/// \param secret The secret to sign with
+/// \param secret_len The length of the secret
+/// \param hash_algorithm The hash algorithm
+/// \param result The signature will be appended to this buffer
+/// \param len If this is non-zero and less than the output size,
+///            the result will be truncated to len bytes
+void signHMAC(const void* data,
+              const size_t data_len,
+              const void* secret,
+              size_t secret_len,
+              const HashAlgorithm hash_algorithm,
+              isc::util::OutputBuffer& result,
+              size_t len = 0);
+
+/// \brief Verify an HMAC signature for the given data
+///
+/// This is a convenience function that verifies an hmac signature,
+/// given a fixed amount of data. Internally it does the same as
+/// creating an HMAC object, feeding it the data, and checking the
+/// resulting signature.
+///
+/// \exception UnsupportedAlgorithm if the given algorithm is unknown
+///            or not supported by the underlying library
+/// \exception BadKey if the given key secret_len is bad
+/// \exception LibraryError if there was any unexpected exception
+///                         in the underlying library
+///
+/// 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 data The data to verify
+/// \param data_len The length of the data
+/// \param secret The secret to sign with
+/// \param secret_len The length of the secret
+/// \param hash_algorithm The hash algorithm
+/// \param sig The signature to verify
+/// \param sig_len The length of the signature
+/// \return True if the signature verifies, false if not
+bool verifyHMAC(const void* data,
+                const size_t data_len,
+                const void* secret,
+                size_t secret_len,
+                const HashAlgorithm hash_algorithm,
+                const void* sig,
+                const size_t sig_len);
+
+/// \brief Delete an HMAC object
+void deleteHMAC(HMAC* hmac);
+
+} // namespace cryptolink
+} // namespace isc
+
+#endif // __ISC_CRYPTO_HMAC
+

+ 75 - 0
src/lib/cryptolink/cryptolink.cc

@@ -0,0 +1,75 @@
+// 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 <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+#include <botan/botan.h>
+
+#include <boost/scoped_ptr.hpp>
+
+using namespace std;
+using namespace isc::util;
+
+
+namespace isc {
+namespace cryptolink {
+
+// For Botan, we use the CryptoLink class object in RAII style
+class CryptoLinkImpl {
+private:
+    Botan::LibraryInitializer botan_init_;
+};
+
+CryptoLink::~CryptoLink() {
+    delete impl_;
+}
+
+CryptoLink&
+CryptoLink::getCryptoLink() {
+    CryptoLink& c = getCryptoLinkInternal();
+    if (c.impl_ == NULL) {
+        c.initialize();
+    }
+    return (c);
+}
+
+CryptoLink&
+CryptoLink::getCryptoLinkInternal() {
+    static CryptoLink instance;
+    return (instance);
+}
+
+void
+CryptoLink::initialize() {
+    CryptoLink& c = getCryptoLinkInternal();
+    if (c.impl_ == NULL) {
+        try {
+            c.impl_ = new CryptoLinkImpl();
+        } catch (const Botan::Exception& ex) {
+            isc_throw(InitializationError, ex.what());
+        }
+    }
+}
+
+HMAC*
+CryptoLink::createHMAC(const void* secret, size_t secret_len,
+                       const HashAlgorithm hash_algorithm)
+{
+    return (new HMAC(secret, secret_len, hash_algorithm));
+}
+
+} // namespace cryptolink
+} // namespace isc
+

+ 204 - 0
src/lib/cryptolink/cryptolink.h

@@ -0,0 +1,204 @@
+// 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.
+
+#ifndef _ISC_CRYPTO_H
+#define _ISC_CRYPTO_H
+
+#include <string>
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <memory>
+
+namespace isc {
+namespace cryptolink {
+
+/// \brief Hash algorithm identifiers
+enum HashAlgorithm {
+    MD5 = 0,            ///< MD5
+    SHA1 = 1,           ///< SHA-1
+    SHA256 = 2,         ///< SHA-256
+    UNKNOWN_HASH = 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.
+};
+
+// Forward declaration for createHMAC()
+class HMAC;
+
+/// General exception class that is the base for all crypto-related
+/// exceptions
+class CryptoLinkError : public Exception {
+public:
+    CryptoLinkError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// This exception is thrown if there was a problem initializing the
+/// crypto library
+class InitializationError : public CryptoLinkError {
+public:
+    InitializationError(const char* file, size_t line, const char* what) :
+        CryptoLinkError(file, line, what) {}
+};
+
+/// This exception is thrown when a cryptographic action is requested
+/// for an algorithm that is not supported by the underlying library.
+class UnsupportedAlgorithm : public CryptoLinkError {
+public:
+    UnsupportedAlgorithm(const char* file, size_t line, const char* what) :
+        CryptoLinkError(file, line, what) {}
+};
+
+/// This exception is thrown when the underlying library could not
+/// handle the key data.
+class BadKey : public CryptoLinkError {
+public:
+    BadKey(const char* file, size_t line, const char* what) :
+        CryptoLinkError(file, line, what) {}
+};
+
+/// This exception is raised when a general error that was not
+/// specifically caught is thrown by the underlying library. It
+/// is replaced by this one so as not have 'external' exceptions
+/// bubbling up
+class LibraryError : public CryptoLinkError {
+public:
+    LibraryError(const char* file, size_t line, const char* what) :
+        CryptoLinkError(file, line, what) {}
+};
+
+/// Forward declaration for pimpl
+class CryptoLinkImpl;
+
+/// \brief Singleton entry point and factory class
+///
+/// This is a singleton class that serves as the entry point to
+/// the underlying cryptography library, and as a factory for objects
+/// within the cryptolink library.
+///
+/// There is only one way to access it, through getCryptoLink(), which
+/// returns a reference to the initialized library. On the first call,
+/// it will be initialized automatically. You can however initialize it
+/// manually through a call to the initalize(), before your first call
+/// to getCryptoLink. Any subsequent call to initialize() will be a
+/// noop.
+///
+/// In order for the CryptoLink library to be sure that the underlying
+/// library has been initialized, and because we do not want to add
+/// such a check to every class and function within it, we have made
+/// the constructors of all classes within cryptolink private. This way
+/// a caller cannot instantiate an object before the library is
+/// initialized, but must use CryptoLink's create method (e.g.
+/// createHMAC()), which enforces (automatic) initialization.
+///
+/// In order for the CryptoLink class to be able to create objects that
+/// have private constructors, it is declared a friend class of these
+/// classes.
+///
+/// Since these factory functions return bare pointers, we also provide
+/// deleter functions for them (e.g. deleteHMAC()), so that a caller
+/// can use that to make sure it uses the correct delete operator (the
+/// one defined at compilation time of this library). A way to make
+/// sure you do not forget this, is to place the result of the create
+/// functions in a shared_ptr with the corresponding deleter function.
+///
+/// \note All other classes within cryptolink should have private
+/// constructors as well, and should have a factory function from
+/// CryptoLink, and a deleter function.
+///
+// Internal note: we can use this class later to initialize and manage
+// dynamic (PKCS#11) libs
+class CryptoLink : private boost::noncopyable {
+public:
+    /// \brief Returns a reference to the singleton instance
+    ///
+    /// If the library has not been initialized yet, it will be
+    /// initialized with some default values.
+    ///
+    /// Since this class is noncopyable, you must use the return
+    /// value directly, or store it in a reference variable.
+    ///
+    /// \exception InitializationError if initialization fails
+    ///
+    /// \return Reference to the singleton instance
+    static CryptoLink& getCryptoLink();
+
+    /// \brief Initialize the library manually
+    ///
+    /// If the library has already been initialized (either by a call
+    /// to initialize() or automatically in getCryptoLink()), this
+    /// function does nothing.
+    ///
+    /// \note A call to initialize() is not strictly necessary with
+    /// the current implementation.
+    ///
+    /// \exception InitializationError if initialization fails
+    ///
+    static void initialize();
+
+    /// \brief Factory function for HMAC objects
+    ///
+    /// CryptoLink objects cannot be constructed directly. This
+    /// function creates a new HMAC object usable for signing or
+    /// verification.
+    ///
+    /// The caller is responsible for deleting the object, and it is
+    /// therefore highly recommended to place the return value of this
+    /// function in a scoped_ptr or shared_ptr.
+    ///
+    /// 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
+    ///
+    /// If you want to safely delete objects created with this method,
+    /// you can use the function deleteHMAC() as defined in
+    /// crypto_hmac.h
+    ///
+    /// \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
+    /// \exception LibraryError if there was any unexpected exception
+    ///                         in the underlying library
+    ///
+    /// \param secret The secret to sign with
+    /// \param secret_len The length of the secret
+    /// \param hash_algorithm The hash algorithm
+    HMAC* createHMAC(const void* secret, size_t secret_len,
+                     const HashAlgorithm hash_algorithm);
+
+private:
+    // To enable us to use an optional explicit initialization call,
+    // the 'real' instance getter is private
+    static CryptoLink& getCryptoLinkInternal();
+
+    // To prevent people constructing their own, we make the constructor
+    // private too.
+    CryptoLink() : impl_(NULL) {}
+    ~CryptoLink();
+
+    CryptoLinkImpl* impl_;
+};
+
+} // namespace cryptolink
+} // namespace isc
+
+#endif // _ISC_CRYPTO_H

+ 26 - 0
src/lib/cryptolink/tests/Makefile.am

@@ -0,0 +1,26 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += crypto_unittests.cc
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+endif
+
+noinst_PROGRAMS = $(TESTS)

+ 511 - 0
src/lib/cryptolink/tests/crypto_unittests.cc

@@ -0,0 +1,511 @@
+// 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 <config.h>
+#include <gtest/gtest.h>
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+using namespace isc::util;
+using namespace isc::cryptolink;
+
+namespace {
+    void checkData(const uint8_t* data, const uint8_t* expected,
+                   size_t len) {
+        for (size_t i = 0; i < len; ++i) {
+            ASSERT_EQ(expected[i], data[i]);
+        }
+    }
+
+    void checkBuffer(const OutputBuffer& buf, const uint8_t* expected,
+                     size_t len)
+    {
+        ASSERT_EQ(len, buf.getLength());
+        checkData(static_cast<const uint8_t*>(buf.getData()), expected,
+                  len);
+    }
+
+    // Sign and verify with the convenience functions
+    void doHMACTestConv(const std::string& data,
+                        const void* secret,
+                        size_t secret_len,
+                        const HashAlgorithm hash_algorithm,
+                        const uint8_t* expected_hmac,
+                        size_t hmac_len) {
+        OutputBuffer data_buf(data.size());
+        data_buf.writeData(data.c_str(), data.size());
+        OutputBuffer hmac_sig(0);
+
+        // Sign it
+        signHMAC(data_buf.getData(), data_buf.getLength(),
+                 secret, secret_len, hash_algorithm, hmac_sig, hmac_len);
+
+        // Check if the signature is what we expect
+        checkBuffer(hmac_sig, expected_hmac, hmac_len);
+
+        // Check whether we can verify it ourselves
+        EXPECT_TRUE(verifyHMAC(data_buf.getData(), data_buf.getLength(),
+                               secret, secret_len, hash_algorithm,
+                               hmac_sig.getData(),
+                               hmac_sig.getLength()));
+
+        // Change the sig by flipping the first octet, and check
+        // whether verification fails then
+        hmac_sig.writeUint8At(~hmac_sig[0], 0);
+        EXPECT_FALSE(verifyHMAC(data_buf.getData(), data_buf.getLength(),
+                               secret, secret_len, hash_algorithm,
+                               hmac_sig.getData(),
+                               hmac_sig.getLength()));
+    }
+
+    // Sign and verify with an instantiation of an HMAC object
+    void doHMACTestDirect(const std::string& data,
+                          const void* secret,
+                          size_t secret_len,
+                          const HashAlgorithm hash_algorithm,
+                          const uint8_t* expected_hmac,
+                          size_t hmac_len) {
+        OutputBuffer data_buf(data.size());
+        data_buf.writeData(data.c_str(), data.size());
+        OutputBuffer hmac_sig(1);
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+        // Sign it
+        boost::shared_ptr<HMAC> hmac_sign(crypto.createHMAC(secret,
+                                                            secret_len,
+                                                            hash_algorithm),
+                                          deleteHMAC);
+        hmac_sign->update(data_buf.getData(), data_buf.getLength());
+        hmac_sign->sign(hmac_sig, hmac_len);
+
+        // Check if the signature is what we expect
+        checkBuffer(hmac_sig, expected_hmac, hmac_len);
+
+        // Check whether we can verify it ourselves
+        boost::shared_ptr<HMAC> hmac_verify(crypto.createHMAC(secret,
+                                                              secret_len,
+                                                              hash_algorithm),
+                                            deleteHMAC);
+        hmac_verify->update(data_buf.getData(), data_buf.getLength());
+        EXPECT_TRUE(hmac_verify->verify(hmac_sig.getData(),
+                                        hmac_sig.getLength()));
+
+        // Change the sig by flipping the first octet, and check
+        // whether verification fails then
+        hmac_sig.writeUint8At(~hmac_sig[0], 0);
+        EXPECT_FALSE(hmac_verify->verify(hmac_sig.getData(),
+                                         hmac_sig.getLength()));
+    }
+
+    void doHMACTestVector(const std::string& data,
+                          const void* secret,
+                          size_t secret_len,
+                          const HashAlgorithm hash_algorithm,
+                          const uint8_t* expected_hmac,
+                          size_t hmac_len) {
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+        boost::shared_ptr<HMAC> hmac_sign(crypto.createHMAC(secret,
+                                                            secret_len,
+                                                            hash_algorithm),
+                                          deleteHMAC);
+        hmac_sign->update(data.c_str(), data.size());
+        std::vector<uint8_t> sig = hmac_sign->sign(hmac_len);
+        ASSERT_EQ(hmac_len, sig.size());
+        checkData(&sig[0], expected_hmac, hmac_len);
+
+        boost::shared_ptr<HMAC> hmac_verify(crypto.createHMAC(secret,
+                                                              secret_len,
+                                                              hash_algorithm),
+                                            deleteHMAC);
+        hmac_verify->update(data.c_str(), data.size());
+        EXPECT_TRUE(hmac_verify->verify(&sig[0], sig.size()));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(hmac_verify->verify(&sig[0], sig.size()));
+    }
+
+    void doHMACTestArray(const std::string& data,
+                         const void* secret,
+                         size_t secret_len,
+                         const HashAlgorithm hash_algorithm,
+                         const uint8_t* expected_hmac,
+                         size_t hmac_len) {
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+        boost::shared_ptr<HMAC> hmac_sign(crypto.createHMAC(secret,
+                                                            secret_len,
+                                                            hash_algorithm),
+                                          deleteHMAC);
+        hmac_sign->update(data.c_str(), data.size());
+
+        // note: this is not exception-safe, and can leak, but
+        // if there is an unexpected exception in the code below we
+        // have more important things to fix.
+        uint8_t* sig = new uint8_t[hmac_len];
+
+        hmac_sign->sign(sig, hmac_len);
+        checkData(sig, expected_hmac, hmac_len);
+
+        boost::shared_ptr<HMAC> hmac_verify(crypto.createHMAC(secret,
+                                                              secret_len,
+                                                              hash_algorithm),
+                                            deleteHMAC);
+        hmac_verify->update(data.c_str(), data.size());
+        EXPECT_TRUE(hmac_verify->verify(sig, hmac_len));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(hmac_verify->verify(sig, hmac_len));
+
+        delete[] sig;
+    }
+
+    void doHMACTest(const std::string& data,
+                    const void* secret,
+                    size_t secret_len,
+                    const HashAlgorithm hash_algorithm,
+                    const uint8_t* expected_hmac,
+                    size_t hmac_len) {
+        doHMACTestConv(data, secret, secret_len, hash_algorithm,
+                       expected_hmac, hmac_len);
+        doHMACTestDirect(data, secret, secret_len, hash_algorithm,
+                         expected_hmac, hmac_len);
+        doHMACTestVector(data, secret, secret_len, hash_algorithm,
+                         expected_hmac, hmac_len);
+        doHMACTestArray(data, secret, secret_len, hash_algorithm,
+                        expected_hmac, hmac_len);
+    }
+}
+
+//
+// Test values taken from RFC 2202
+//
+TEST(CryptoLinkTest, HMAC_MD5_RFC2202_SIGN) {
+    const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+                               0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+                               0x0b, 0x0b };
+    const uint8_t hmac_expected[] = { 0x92, 0x94, 0x72, 0x7a, 0x36,
+                                      0x38, 0xbb, 0x1c, 0x13, 0xf4,
+                                      0x8e, 0xf8, 0x15, 0x8b, 0xfc,
+                                      0x9d };
+    doHMACTest("Hi There", secret, 16, MD5, hmac_expected, 16);
+
+    const uint8_t hmac_expected2[] = { 0x75, 0x0c, 0x78, 0x3e, 0x6a,
+                                       0xb0, 0xb5, 0x03, 0xea, 0xa8,
+                                       0x6e, 0x31, 0x0a, 0x5d, 0xb7,
+                                       0x38 };
+    doHMACTest("what do ya want for nothing?", "Jefe", 4, MD5,
+               hmac_expected2, 16);
+
+    const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+                                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+                                0xaa, 0xaa, 0xaa, 0xaa };
+    const uint8_t hmac_expected3[] = { 0x56, 0xbe, 0x34, 0x52, 0x1d,
+                                       0x14, 0x4c, 0x88, 0xdb, 0xb8,
+                                       0xc7, 0x33, 0xf0, 0xe8, 0xb3,
+                                       0xf6};
+    doHMACTest(std::string(50, 0xdd), secret3, 16, MD5, hmac_expected3, 16);
+
+    const std::string data4(50, 0xcd);
+    const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                                0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+                                0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+                                0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+                                0x19 };
+    const uint8_t hmac_expected4[] = { 0x69, 0x7e, 0xaf, 0x0a, 0xca,
+                                       0x3a, 0x3a, 0xea, 0x3a, 0x75,
+                                       0x16, 0x47, 0x46, 0xff, 0xaa,
+                                       0x79 };
+    doHMACTest(data4, secret4, 25, MD5, hmac_expected4, 16);
+
+    const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                                0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                                0x0c, 0x0c, 0x0c, 0x0c };
+    const uint8_t hmac_expected5[] = { 0x56, 0x46, 0x1e, 0xf2, 0x34,
+                                       0x2e, 0xdc, 0x00, 0xf9, 0xba,
+                                       0xb9, 0x95, 0x69, 0x0e, 0xfd,
+                                       0x4c };
+    doHMACTest("Test With Truncation", secret5, 16, MD5,
+               hmac_expected5, 16);
+    doHMACTest("Test With Truncation", secret5, 16, MD5,
+               hmac_expected5, 12);
+
+    const uint8_t hmac_expected6[] = { 0x6b, 0x1a, 0xb7, 0xfe, 0x4b,
+                                       0xd7, 0xbf, 0x8f, 0x0b, 0x62,
+                                       0xe6, 0xce, 0x61, 0xb9, 0xd0,
+                                       0xcd };
+    doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First",
+               std::string(80, 0xaa).c_str(), 80, MD5, hmac_expected6, 16);
+
+    const uint8_t hmac_expected7[] = { 0x6f, 0x63, 0x0f, 0xad, 0x67,
+                                       0xcd, 0xa0, 0xee, 0x1f, 0xb1,
+                                       0xf5, 0x62, 0xdb, 0x3a, 0xa5,
+                                       0x3e };
+    doHMACTest("Test Using Larger Than Block-Size Key and Larger Than "
+               "One Block-Size Data",
+               std::string(80, 0xaa).c_str(), 80, MD5, hmac_expected7, 16);
+}
+
+//
+// Test values taken from RFC 2202
+//
+TEST(CryptoLinkTest, HMAC_SHA1_RFC2202_SIGN) {
+    const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+                               0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+                               0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b };
+    const uint8_t hmac_expected[] = { 0xb6, 0x17, 0x31, 0x86, 0x55,
+                                      0x05, 0x72, 0x64, 0xe2, 0x8b,
+                                      0xc0, 0xb6, 0xfb, 0x37, 0x8c,
+                                      0x8e, 0xf1, 0x46, 0xbe, 0x00 };
+    doHMACTest("Hi There", secret, 20, SHA1, hmac_expected, 20);
+
+    const uint8_t hmac_expected2[] = { 0xef, 0xfc, 0xdf, 0x6a, 0xe5,
+                                       0xeb, 0x2f, 0xa2, 0xd2, 0x74,
+                                       0x16, 0xd5, 0xf1, 0x84, 0xdf,
+                                       0x9c, 0x25, 0x9a, 0x7c, 0x79 };
+    doHMACTest("what do ya want for nothing?", "Jefe", 4, SHA1,
+               hmac_expected2, 20);
+
+    const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+                                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+                                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+                                0xaa, 0xaa };
+    const uint8_t hmac_expected3[] = { 0x12, 0x5d, 0x73, 0x42, 0xb9,
+                                       0xac, 0x11, 0xcd, 0x91, 0xa3,
+                                       0x9a, 0xf4, 0x8a, 0xa1, 0x7b,
+                                       0x4f, 0x63, 0xf1, 0x75, 0xd3 };
+    doHMACTest(std::string(50, 0xdd), secret3, 20, SHA1, hmac_expected3, 20);
+
+    const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                                0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+                                0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+                                0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+                                0x19 };
+    const uint8_t hmac_expected4[] = { 0x4c, 0x90, 0x07, 0xf4, 0x02,
+                                       0x62, 0x50, 0xc6, 0xbc, 0x84,
+                                       0x14, 0xf9, 0xbf, 0x50, 0xc8,
+                                       0x6c, 0x2d, 0x72, 0x35, 0xda };
+    doHMACTest(std::string(50, 0xcd), secret4, 25, SHA1, hmac_expected4, 20);
+
+    const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                                0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                                0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                                0x0c, 0x0c };
+    const uint8_t hmac_expected5[] = { 0x4c, 0x1a, 0x03, 0x42, 0x4b,
+                                       0x55, 0xe0, 0x7f, 0xe7, 0xf2,
+                                       0x7b, 0xe1, 0xd5, 0x8b, 0xb9,
+                                       0x32, 0x4a, 0x9a, 0x5a, 0x04 };
+    doHMACTest("Test With Truncation", secret5, 20, SHA1,
+               hmac_expected5, 20);
+    doHMACTest("Test With Truncation", secret5, 20, SHA1,
+               hmac_expected5, 12);
+
+    const uint8_t hmac_expected6[] = { 0xaa, 0x4a, 0xe5, 0xe1, 0x52,
+                                       0x72, 0xd0, 0x0e, 0x95, 0x70,
+                                       0x56, 0x37, 0xce, 0x8a, 0x3b,
+                                       0x55, 0xed, 0x40, 0x21, 0x12 };
+    doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First",
+               std::string(80, 0xaa).c_str(), 80, SHA1, hmac_expected6, 20);
+
+    const uint8_t hmac_expected7[] = { 0xe8, 0xe9, 0x9d, 0x0f, 0x45,
+                                       0x23, 0x7d, 0x78, 0x6d, 0x6b,
+                                       0xba, 0xa7, 0x96, 0x5c, 0x78,
+                                       0x08, 0xbb, 0xff, 0x1a, 0x91 };
+    doHMACTest("Test Using Larger Than Block-Size Key and Larger Than "
+               "One Block-Size Data",
+               std::string(80, 0xaa).c_str(), 80, SHA1, hmac_expected7, 20);
+}
+
+//
+// Test values taken from RFC 4231
+//
+TEST(CryptoLinkTest, HMAC_SHA256_RFC2202_SIGN) {
+    const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+                               0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+                               0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b };
+    const uint8_t hmac_expected[] = { 0xb0, 0x34, 0x4c, 0x61, 0xd8,
+                                      0xdb, 0x38, 0x53, 0x5c, 0xa8,
+                                      0xaf, 0xce, 0xaf, 0x0b, 0xf1,
+                                      0x2b, 0x88, 0x1d, 0xc2, 0x00,
+                                      0xc9, 0x83, 0x3d, 0xa7, 0x26,
+                                      0xe9, 0x37, 0x6c, 0x2e, 0x32,
+                                      0xcf, 0xf7 };
+    doHMACTest("Hi There", secret, 20, SHA256, hmac_expected, 32);
+
+    const uint8_t hmac_expected2[] = { 0x5b, 0xdc, 0xc1, 0x46, 0xbf,
+                                       0x60, 0x75, 0x4e, 0x6a, 0x04,
+                                       0x24, 0x26, 0x08, 0x95, 0x75,
+                                       0xc7, 0x5a, 0x00, 0x3f, 0x08,
+                                       0x9d, 0x27, 0x39, 0x83, 0x9d,
+                                       0xec, 0x58, 0xb9, 0x64, 0xec,
+                                       0x38, 0x43 };
+    doHMACTest("what do ya want for nothing?", "Jefe", 4, SHA256,
+               hmac_expected2, 32);
+
+    const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+                                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+                                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+                                0xaa, 0xaa };
+    const uint8_t hmac_expected3[] = { 0x77, 0x3e, 0xa9, 0x1e, 0x36,
+                                       0x80, 0x0e, 0x46, 0x85, 0x4d,
+                                       0xb8, 0xeb, 0xd0, 0x91, 0x81,
+                                       0xa7, 0x29, 0x59, 0x09, 0x8b,
+                                       0x3e, 0xf8, 0xc1, 0x22, 0xd9,
+                                       0x63, 0x55, 0x14, 0xce, 0xd5,
+                                       0x65, 0xfe };
+    doHMACTest(std::string(50, 0xdd), secret3, 20, SHA256, hmac_expected3, 32);
+
+    const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                                0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+                                0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+                                0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+                                0x19 };
+    const uint8_t hmac_expected4[] = { 0x82, 0x55, 0x8a, 0x38, 0x9a,
+                                       0x44, 0x3c, 0x0e, 0xa4, 0xcc,
+                                       0x81, 0x98, 0x99, 0xf2, 0x08,
+                                       0x3a, 0x85, 0xf0, 0xfa, 0xa3,
+                                       0xe5, 0x78, 0xf8, 0x07, 0x7a,
+                                       0x2e, 0x3f, 0xf4, 0x67, 0x29,
+                                       0x66, 0x5b };
+    doHMACTest(std::string(50, 0xcd), secret4, 25, SHA256, hmac_expected4, 32);
+
+    const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                                0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                                0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+                                0x0c, 0x0c };
+    const uint8_t hmac_expected5[] = { 0xa3, 0xb6, 0x16, 0x74, 0x73,
+                                       0x10, 0x0e, 0xe0, 0x6e, 0x0c,
+                                       0x79, 0x6c, 0x29, 0x55, 0x55,
+                                       0x2b };
+    doHMACTest("Test With Truncation", secret5, 20, SHA256,
+               hmac_expected5, 16);
+
+    const uint8_t hmac_expected6[] = { 0x60, 0xe4, 0x31, 0x59, 0x1e,
+                                       0xe0, 0xb6, 0x7f, 0x0d, 0x8a,
+                                       0x26, 0xaa, 0xcb, 0xf5, 0xb7,
+                                       0x7f, 0x8e, 0x0b, 0xc6, 0x21,
+                                       0x37, 0x28, 0xc5, 0x14, 0x05,
+                                       0x46, 0x04, 0x0f, 0x0e, 0xe3,
+                                       0x7f, 0x54 };
+    doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First",
+               std::string(131, 0xaa).c_str(), 131, SHA256, hmac_expected6, 32);
+
+    const uint8_t hmac_expected7[] = { 0x9b, 0x09, 0xff, 0xa7, 0x1b,
+                                       0x94, 0x2f, 0xcb, 0x27, 0x63,
+                                       0x5f, 0xbc, 0xd5, 0xb0, 0xe9,
+                                       0x44, 0xbf, 0xdc, 0x63, 0x64,
+                                       0x4f, 0x07, 0x13, 0x93, 0x8a,
+                                       0x7f, 0x51, 0x53, 0x5c, 0x3a,
+                                       0x35, 0xe2 };
+    doHMACTest("This is a test using a larger than block-size key and a"
+               " larger than block-size data. The key needs to be hashe"
+               "d before being used by the HMAC algorithm.",
+               std::string(131, 0xaa).c_str(), 131, SHA256, hmac_expected7, 32);
+}
+
+namespace {
+    size_t
+    sigVectorLength(HashAlgorithm alg, size_t len) {
+        boost::shared_ptr<HMAC> hmac_sign(
+            CryptoLink::getCryptoLink().createHMAC("asdf", 4, alg),
+            deleteHMAC);
+        hmac_sign->update("asdf", 4);
+        const std::vector<uint8_t> sig = hmac_sign->sign(len);
+        return (sig.size());
+    }
+
+    size_t
+    sigBufferLength(HashAlgorithm alg, size_t len) {
+        boost::shared_ptr<HMAC> hmac_sign(
+            CryptoLink::getCryptoLink().createHMAC("asdf", 4, alg),
+            deleteHMAC);
+        hmac_sign->update("asdf", 4);
+        OutputBuffer sig(0);
+        hmac_sign->sign(sig, len);
+        return (sig.getLength());
+    }
+}
+
+TEST(CryptoLinkTest, HMACSigLengthArgument) {
+    std::vector<uint8_t> sig;
+
+    EXPECT_EQ(16, sigVectorLength(MD5, 0));
+    EXPECT_EQ(8, sigVectorLength(MD5, 8));
+    EXPECT_EQ(16, sigVectorLength(MD5, 16));
+    EXPECT_EQ(16, sigVectorLength(MD5, 40));
+    EXPECT_EQ(16, sigVectorLength(MD5, 2000));
+
+    EXPECT_EQ(20, sigBufferLength(SHA1, 0));
+    EXPECT_EQ(8, sigBufferLength(SHA1, 8));
+    EXPECT_EQ(20, sigBufferLength(SHA1, 20));
+    EXPECT_EQ(20, sigBufferLength(SHA1, 40));
+    EXPECT_EQ(20, sigBufferLength(SHA1, 2000));
+
+    EXPECT_EQ(32, sigBufferLength(SHA256, 0));
+    EXPECT_EQ(8, sigBufferLength(SHA256, 8));
+    EXPECT_EQ(32, sigBufferLength(SHA256, 32));
+    EXPECT_EQ(32, sigBufferLength(SHA256, 40));
+    EXPECT_EQ(32, sigBufferLength(SHA256, 3200));
+
+    EXPECT_EQ(16, sigBufferLength(MD5, 0));
+    EXPECT_EQ(8, sigBufferLength(MD5, 8));
+    EXPECT_EQ(16, sigBufferLength(MD5, 16));
+    EXPECT_EQ(16, sigBufferLength(MD5, 40));
+    EXPECT_EQ(16, sigBufferLength(MD5, 2000));
+
+    EXPECT_EQ(20, sigBufferLength(SHA1, 0));
+    EXPECT_EQ(8, sigBufferLength(SHA1, 8));
+    EXPECT_EQ(20, sigBufferLength(SHA1, 20));
+    EXPECT_EQ(20, sigBufferLength(SHA1, 40));
+    EXPECT_EQ(20, sigBufferLength(SHA1, 2000));
+
+    EXPECT_EQ(32, sigBufferLength(SHA256, 0));
+    EXPECT_EQ(8, sigBufferLength(SHA256, 8));
+    EXPECT_EQ(32, sigBufferLength(SHA256, 32));
+    EXPECT_EQ(32, sigBufferLength(SHA256, 40));
+    EXPECT_EQ(32, sigBufferLength(SHA256, 3200));
+}
+
+TEST(CryptoLinkTest, BadKey) {
+    OutputBuffer data_buf(0);
+    OutputBuffer hmac_sig(0);
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+    EXPECT_THROW(crypto.createHMAC(NULL, 0, MD5), BadKey);
+    EXPECT_THROW(crypto.createHMAC(NULL, 0, UNKNOWN_HASH), UnsupportedAlgorithm);
+
+    EXPECT_THROW(signHMAC(data_buf.getData(), data_buf.getLength(),
+                          NULL, 0, MD5, hmac_sig), BadKey);
+    EXPECT_THROW(signHMAC(data_buf.getData(), data_buf.getLength(),
+                          NULL, 0, UNKNOWN_HASH, hmac_sig),
+                          UnsupportedAlgorithm);
+
+    EXPECT_THROW(verifyHMAC(data_buf.getData(), data_buf.getLength(),
+                            NULL, 0, MD5, hmac_sig.getData(),
+                            hmac_sig.getLength()), BadKey);
+    EXPECT_THROW(verifyHMAC(data_buf.getData(), data_buf.getLength(),
+                            NULL, 0, UNKNOWN_HASH, hmac_sig.getData(),
+                            hmac_sig.getLength()),
+                            UnsupportedAlgorithm);
+}
+
+TEST(CryptoLinkTest, Singleton) {
+    const CryptoLink& c1 = CryptoLink::getCryptoLink();
+    const CryptoLink& c2 = CryptoLink::getCryptoLink();
+    ASSERT_EQ(&c1, &c2);
+}

+ 22 - 0
src/lib/cryptolink/tests/run_unittests.cc

@@ -0,0 +1,22 @@
+// 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 <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    return (RUN_ALL_TESTS());
+}

+ 22 - 0
src/lib/dns/python/tests/tsigkey_python_test.py

@@ -44,6 +44,28 @@ class TSIGKeyTest(unittest.TestCase):
                           TSIGKey.HMACMD5_NAME,
                           'should be binary') # signature mismatch
 
+    def test_str(self):
+        k1 = TSIGKey('test.example:CwsLCwsLCwsLCwsLCwsLCw==:hmac-md5.sig-alg.reg.int')
+        self.assertEqual(Name('test.example.'), k1.get_key_name())
+        self.assertEqual(Name('hmac-md5.sig-alg.reg.int.'), k1.get_algorithm_name())
+        self.assertEqual(b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b',
+                         k1.get_secret())
+        self.assertEqual('test.example.:CwsLCwsLCwsLCwsLCwsLCw==:hmac-md5.sig-alg.reg.int.',
+                         k1.to_text())
+
+        self.assertRaises(InvalidParameter, TSIGKey,
+                          'test.example:CwsLCwsLCwsLCwsLCwsLCw==:unsupported')
+        self.assertRaises(InvalidParameter, TSIGKey,
+                          '::')
+        self.assertRaises(InvalidParameter, TSIGKey,
+                          'test.example:')
+        self.assertRaises(InvalidParameter, TSIGKey,
+                          'test.example:%bad_base_64%')
+        self.assertRaises(InvalidParameter, TSIGKey,
+                          'test.example:CwsLCwsLCwsLCwsLCwsLCw==:')
+        self.assertRaises(InvalidParameter, TSIGKey,
+                          'test.:example:CwsLCwsLCwsLCwsLCwsLCw==')
+
 class TSIGKeyRingTest(unittest.TestCase):
     key_name = Name('example.com')
     secret = b'someRandomData'

+ 27 - 13
src/lib/dns/python/tsigkey_python.cc

@@ -55,6 +55,7 @@ void TSIGKey_destroy(s_TSIGKey* self);
 PyObject* TSIGKey_getKeyName(const s_TSIGKey* self);
 PyObject* TSIGKey_getAlgorithmName(const s_TSIGKey* self);
 PyObject* TSIGKey_getSecret(const s_TSIGKey* self);
+PyObject* TSIGKey_toText(const s_TSIGKey* self);
 
 // This list contains the actual set of functions we have in
 // python. Each entry has
@@ -72,6 +73,8 @@ PyMethodDef TSIGKey_methods[] = {
     { "get_secret",
       reinterpret_cast<PyCFunction>(TSIGKey_getSecret), METH_NOARGS,
       "Return the value of the TSIG secret." },
+    { "to_text", reinterpret_cast<PyCFunction>(TSIGKey_toText), METH_NOARGS,
+      "Returns the string representation (name:secret:algorithm)" },
     { NULL, NULL, 0, NULL }
 };
 
@@ -148,27 +151,33 @@ createNameObject(const Name& source) {
 
 int
 TSIGKey_init(s_TSIGKey* self, PyObject* args) {
+    const char* str;
+
     const s_Name* key_name;
     const s_Name* algorithm_name;
     PyObject* bytes_obj;
     const char* secret;
     Py_ssize_t secret_len;
 
-    if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name,
+
+    try {
+        if (PyArg_ParseTuple(args, "s", &str)) {
+            self->tsigkey = new TSIGKey(str);
+            return (0);
+        } else if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name,
                          &name_type, &algorithm_name, &bytes_obj) &&
-        PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) != -1) {
-        try {
-            self->tsigkey = new TSIGKey(*key_name->name,
-                                        *algorithm_name->name,
-                                        secret, secret_len);
-        } catch (const isc::InvalidParameter& ex) {
-            PyErr_SetString(po_InvalidParameter, ex.what());
-            return (-1);
-        } catch (...) {
-            PyErr_SetString(po_IscException, "Unexpected exception");
-            return (-1);
+            PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) != -1) {
+                self->tsigkey = new TSIGKey(*key_name->name,
+                                            *algorithm_name->name,
+                                            secret, secret_len);
+            return (0);
         }
-        return (0);
+    } catch (const isc::InvalidParameter& ex) {
+        PyErr_SetString(po_InvalidParameter, ex.what());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(po_IscException, "Unexpected exception");
+        return (-1);
     }
 
     PyErr_Clear();
@@ -201,6 +210,11 @@ TSIGKey_getSecret(const s_TSIGKey* const self) {
                           self->tsigkey->getSecretLength()));
 }
 
+PyObject*
+TSIGKey_toText(const s_TSIGKey* self) {
+    return (Py_BuildValue("s", self->tsigkey->toText().c_str()));
+}
+
 // Module Initialization, all statics are initialized here
 bool
 initModulePart_TSIGKey(PyObject* mod) {

+ 27 - 0
src/lib/dns/tests/tsigkey_unittest.cc

@@ -227,4 +227,31 @@ TEST_F(TSIGKeyRingTest, findFromSome) {
               keyring.find(Name("noexist.example")).key);
 }
 
+TEST(TSIGStringTest, TSIGKeyFromToString) {
+    TSIGKey k1 = TSIGKey("test.example:MSG6Ng==:hmac-md5.sig-alg.reg.int");
+    TSIGKey k2 = TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.");
+    TSIGKey k3 = TSIGKey("test.example:MSG6Ng==");
+    TSIGKey k4 = TSIGKey(Name("test.example."), Name("hmac-sha1."), NULL, 0);
+
+    EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.",
+              k1.toText());
+    EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.",
+              k2.toText());
+    EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.",
+              k3.toText());
+    EXPECT_EQ("test.example.::hmac-sha1.", k4.toText());
+
+    EXPECT_THROW(TSIGKey(""), isc::InvalidParameter);
+    EXPECT_THROW(TSIGKey(":"), isc::InvalidParameter);
+    EXPECT_THROW(TSIGKey("::"), isc::InvalidParameter);
+    EXPECT_THROW(TSIGKey("..:aa:"), isc::InvalidParameter);
+    EXPECT_THROW(TSIGKey("test.example:xxxx:"), isc::InvalidParameter);
+    EXPECT_THROW(TSIGKey("test.example.::"), isc::InvalidParameter);
+    EXPECT_THROW(TSIGKey("test.example.:"), isc::InvalidParameter);
+    EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:"), isc::InvalidParameter);
+    EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:unknown"), isc::InvalidParameter);
+
+}
+
+
 } // end namespace

+ 68 - 3
src/lib/dns/tsigkey.cc

@@ -15,14 +15,26 @@
 #include <map>
 #include <utility>
 #include <vector>
+#include <sstream>
 
 #include <exceptions/exceptions.h>
 
+#include <cryptolink/cryptolink.h>
+
 #include <dns/name.h>
+#include <util/encode/base64.h>
 #include <dns/tsigkey.h>
 
 using namespace std;
 
+namespace {
+    bool isValidAlgorithmName(const isc::dns::Name& name) {
+        return (name == isc::dns::TSIGKey::HMACMD5_NAME() ||
+                name == isc::dns::TSIGKey::HMACSHA1_NAME() ||
+                name == isc::dns::TSIGKey::HMACSHA256_NAME());
+    }
+}
+
 namespace isc {
 namespace dns {
 struct
@@ -44,9 +56,7 @@ TSIGKey::TSIGKeyImpl {
 TSIGKey::TSIGKey(const Name& key_name, const Name& algorithm_name,
                  const void* secret, size_t secret_len) : impl_(NULL)
 {
-    if (algorithm_name != HMACMD5_NAME() &&
-        algorithm_name != HMACSHA1_NAME() &&
-        algorithm_name != HMACSHA256_NAME()) {
+    if (!isValidAlgorithmName(algorithm_name)) {
         isc_throw(InvalidParameter, "Unknown TSIG algorithm is specified: " <<
                   algorithm_name);
     }
@@ -59,6 +69,50 @@ TSIGKey::TSIGKey(const Name& key_name, const Name& algorithm_name,
     impl_ = new TSIGKeyImpl(key_name, algorithm_name, secret, secret_len);
 }
 
+TSIGKey::TSIGKey(const std::string& str) : impl_(NULL) {
+    try {
+        istringstream iss(str);
+
+        string keyname_str;
+        getline(iss, keyname_str, ':');
+        if (iss.fail() || iss.bad() || iss.eof()) {
+            isc_throw(InvalidParameter, "Invalid TSIG key string: " << str);
+        }
+
+        string secret_str;
+        getline(iss, secret_str, ':');
+        if (iss.fail() || iss.bad()) {
+            isc_throw(InvalidParameter, "Invalid TSIG key string: " << str);
+        }
+
+        string algo_str;
+        if (!iss.eof()) {
+            getline(iss, algo_str);
+        }
+        if (iss.fail() || iss.bad()) {
+            isc_throw(InvalidParameter, "Invalid TSIG key string: " << str);
+        }
+
+        const Name algo_name(algo_str.empty() ? "hmac-md5.sig-alg.reg.int" :
+                             algo_str);
+        if (!isValidAlgorithmName(algo_name)) {
+            isc_throw(InvalidParameter, "Unknown TSIG algorithm is specified: " <<
+                      algo_name);
+        }
+
+        vector<uint8_t> secret;
+        isc::util::encode::decodeBase64(secret_str, secret);
+
+        impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, &secret[0],
+                                secret.size());
+    } catch (const Exception& e) {
+        // 'reduce' the several types of exceptions name parsing and
+        // Base64 decoding can throw to just the InvalidParameter
+        isc_throw(InvalidParameter, e.what());
+    }
+}
+
+
 TSIGKey::TSIGKey(const TSIGKey& source) : impl_(new TSIGKeyImpl(*source.impl_))
 {}
 
@@ -99,6 +153,17 @@ TSIGKey::getSecretLength() const {
     return (impl_->secret_.size());
 }
 
+std::string
+TSIGKey::toText() const {
+    const vector<uint8_t> secret_v(static_cast<const uint8_t*>(getSecret()),
+                                   static_cast<const uint8_t*>(getSecret()) +
+                                   getSecretLength());
+    std::string secret_str = isc::util::encode::encodeBase64(secret_v);
+
+    return (getKeyName().toText() + ":" + secret_str + ":" +
+            getAlgorithmName().toText());
+}
+
 const
 Name& TSIGKey::HMACMD5_NAME() {
     static Name alg_name("hmac-md5.sig-alg.reg.int");

+ 31 - 0
src/lib/dns/tsigkey.h

@@ -90,6 +90,25 @@ public:
     TSIGKey(const Name& key_name, const Name& algorithm_name,
             const void* secret, size_t secret_len);
 
+    /// \brief Constructor from an input string
+    ///
+    /// The string must be of the form:
+    /// <name>:<secret>[:<algorithm>]
+    /// Where <name> is a domain name for the key, <secret> is a
+    /// base64 representation of the key secret, and the optional
+    /// algorithm is an algorithm identifier as specified in RFC4635
+    /// The default algorithm is hmac-md5.sig-alg.reg.int.
+    ///
+    /// Since ':' is used as a separator here, it is not possible to
+    /// use this constructor to create keys with a ':' character in
+    /// their name.
+    ///
+    /// \exception InvalidParameter exception if the input string is
+    /// invalid.
+    ///
+    /// \param str The string to make a TSIGKey from
+    explicit TSIGKey(const std::string& str);
+
     /// \brief The copy constructor.
     ///
     /// It internally allocates a resource, and if it fails a corresponding
@@ -139,6 +158,18 @@ public:
     const void* getSecret() const;
     //@}
 
+    /// \brief Converts the TSIGKey to a string value
+    ///
+    /// The resulting string will be of the form
+    /// name:secret:algorithm
+    /// Where <name> is a domain name for the key, <secret> is a
+    /// base64 representation of the key secret, and algorithm is
+    /// an algorithm identifier as specified in RFC4635
+    ///
+    /// \param key the TSIG key to convert
+    /// \return The string representation of the given TSIGKey.
+    std::string toText() const;
+
     ///
     /// \name Well known algorithm names as defined in RFC2845 and RFC4635.
     ///