BIND 10 master, updated. 0310b8b77e6adb8671d1f9ebfe826e394e95f53f Merge branch 'master' of ssh://bind10.isc.org/var/bind10/git/bind10
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Apr 28 08:20:37 UTC 2011
The branch, master has been updated
via 0310b8b77e6adb8671d1f9ebfe826e394e95f53f (commit)
via d9433ad4bd9aec1028b9b05aa55e9b083e35a4d4 (commit)
via 9df42279a47eb617f586144dce8cce680598558a (commit)
via 05c6223cf7f145ec6e96cffed0561a03387b7367 (commit)
via 7be4a7e7f859881a3e5a3f4268dbdbc3468b7214 (commit)
via 6c274fd95d3c2bcc99113108f5a68aa0364f924c (commit)
via a1703e5ae5fc9458c066fb4aab7666bf4e5fdb8a (commit)
via 145a6173191e6cd39be3d495a6e5d0511dfe867a (commit)
via fb7877b06ba873d4fb222409dd92b4701ae11ffb (commit)
via 20336851f494ff1d9c8937400a99ed4d0203a340 (commit)
via 246e56b3bb7792789b3aa891e21e580976d9e7be (commit)
via 168bed01625dd25fc2c40bd6b402763849e840ec (commit)
via e831ffa2129fc5e013a0217f004896f6f40f258e (commit)
via 4eda7bbc802b24730eb8ef9e5716fc47f19d5a00 (commit)
via 122d477847f80c7d77bec087a849b9e6743c17d3 (commit)
via e74f6d5e8f9abb081bde606bc46319488c7e546b (commit)
via f137e0fa3a387fcc13898ea6da6b61dcd80dc8ef (commit)
via cf456aa1546145ac403e75535e555d773b39ff20 (commit)
via 006b2ea9dabd869bc05682b73b083ea815d520db (commit)
via c34d8eb763ceb840f65de77a22eb67c8b1ab6392 (commit)
via 2aa47d167c58b6718706f8e037927b824ff0cf00 (commit)
via 604e90d1e0c928cc8d94146beea6e24fe2252eec (commit)
via dcf58b7836e854107438368905576de7d29e0e3b (commit)
via f4f11a9b1c09adb07e2d6b99a0bd342d5a75ea3f (commit)
via 74e6966d7f8f8216dad871f1648cab977fb9fea2 (commit)
via 0334c1798ea43061494a530d8bc274283f1f1fc7 (commit)
via 40f4efbbd9f5685147cd1abf19d12c336bc1b8d9 (commit)
via a57662d6db020dd0b334472b5bb7ea88aefb6a7d (commit)
via 122d9611bd552c3113eca8aa5e91d2b8a3018210 (commit)
via 231b29911894b0f5ddbd0d1444d9dfdf9cb5abb3 (commit)
via 4d55533790df1992aee5c088d4f91593d055c080 (commit)
via c374a5c5a329a307346be02d5b884ab57e1cb8c1 (commit)
via cd3ce64ac0ac3e82ba905f3eea77f1040b7e69b8 (commit)
via 24aa99548a4164d2c57adcf3829cc50f1fd34de5 (commit)
via aa0ddfbb4f0aa61cfee383f5459c2183b353674b (commit)
via 4f1d8d0f2ce99880306f7926a23b28f1aa8e4fd9 (commit)
via a7c4e0bc10a5430008d12081f4cb30d1d0d26723 (commit)
via 3987357f56e0fc2feca75001b963a11f1b7120ff (commit)
via 6cece2fc1d8a086b65421a583cf35b40f6ee1751 (commit)
via 400d3b61a32c0fc47c5f41a93ea649fbde6a5445 (commit)
via fac1bf01a628b3b922af67cdd94f05257b6eef9a (commit)
via 34ba5055b70c9de09d406b3e51ad2e02b98f6439 (commit)
via 238dca82160452df0eb732ce014166777380767e (commit)
via 253d063204f1e0656c89650999c76f942b4840f6 (commit)
via 817462f1ea1f1e9b4e05b2da90b8d311a7b59843 (commit)
via 30ab4cfbd7f0744653248efc5635c68930359eea (commit)
via 97feb3a88bae42f4051cd67384a4dc7dcd018c9a (commit)
via b78eefc13bc53a301327674a10d88cb917d6f28d (commit)
via 34d2f102aedbdb6b818f8eb8deb1e00fe431dae4 (commit)
via 6eab8b3fc5922c7b9c6403f4510b929544eafe30 (commit)
via e29c4f167f9d466eb9310989e836a0be428e5ace (commit)
via 992f3702aad9c83836af20d32e96313ff22cde3b (commit)
via 150914707089969bde30fbc6276abc00f3211ba3 (commit)
from 87e4b9874ac16ccc5d236a2fcb8221942713e086 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 0310b8b77e6adb8671d1f9ebfe826e394e95f53f
Merge: d9433ad4bd9aec1028b9b05aa55e9b083e35a4d4 87e4b9874ac16ccc5d236a2fcb8221942713e086
Author: Jelte Jansen <jelte at isc.org>
Date: Thu Apr 28 10:04:40 2011 +0200
Merge branch 'master' of ssh://bind10.isc.org/var/bind10/git/bind10
commit d9433ad4bd9aec1028b9b05aa55e9b083e35a4d4
Author: Jelte Jansen <jelte at isc.org>
Date: Wed Apr 27 10:56:02 2011 +0200
[master] updated changelog
commit 9df42279a47eb617f586144dce8cce680598558a
Merge: 4903de33e8f5d16ed99b6ad6b22fac023e653f71 05c6223cf7f145ec6e96cffed0561a03387b7367
Author: Jelte Jansen <jelte at isc.org>
Date: Wed Apr 27 10:35:13 2011 +0200
[master] Merge branch 'trac781'
Conflicts:
configure.ac
doc/Doxyfile
src/lib/Makefile.am
Also updated includes (because of the lib/util move)
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 7 +
configure.ac | 44 ++-
doc/Doxyfile | 11 +-
src/lib/Makefile.am | 5 +-
src/lib/cryptolink/Makefile.am | 12 +
src/lib/cryptolink/crypto_hmac.cc | 237 +++++++++
src/lib/cryptolink/crypto_hmac.h | 209 ++++++++
src/lib/cryptolink/cryptolink.cc | 75 +++
src/lib/cryptolink/cryptolink.h | 204 ++++++++
.../{exceptions => cryptolink}/tests/Makefile.am | 11 +-
src/lib/cryptolink/tests/crypto_unittests.cc | 511 ++++++++++++++++++++
.../{util => cryptolink}/tests/run_unittests.cc | 1 +
src/lib/dns/python/tests/tsigkey_python_test.py | 22 +
src/lib/dns/python/tsigkey_python.cc | 40 +-
src/lib/dns/tests/tsigkey_unittest.cc | 27 +
src/lib/dns/tsigkey.cc | 71 +++-
src/lib/dns/tsigkey.h | 31 ++
17 files changed, 1486 insertions(+), 32 deletions(-)
create mode 100644 src/lib/cryptolink/Makefile.am
create mode 100644 src/lib/cryptolink/crypto_hmac.cc
create mode 100644 src/lib/cryptolink/crypto_hmac.h
create mode 100644 src/lib/cryptolink/cryptolink.cc
create mode 100644 src/lib/cryptolink/cryptolink.h
copy src/lib/{exceptions => cryptolink}/tests/Makefile.am (61%)
create mode 100644 src/lib/cryptolink/tests/crypto_unittests.cc
copy src/lib/{util => cryptolink}/tests/run_unittests.cc (99%)
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 87f6b15..666d1f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+226. [func]* jelte
+ Introduced an API for cryptographic operations. Currently it only
+ supports HMAC, intended for use with TSIG. The current
+ implementation uses Botan as the backend library.
+ This introduces a new dependency, on Botan.
+ (Trac#781, git 9df42279a47eb617f586144dce8cce680598558a)
+
225. [func] naokikambe
Added the HTTP/XML interface(b10-stats-httpd) to the statistics feature
in BIND 10. b10-stats-httpd is a standalone HTTP server and it requests
diff --git a/configure.ac b/configure.ac
index 82bd3d7..a539f17 100644
--- a/configure.ac
+++ b/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
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 83e85b8..a57d275 100644
--- a/doc/Doxyfile
+++ b/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
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 81455c4..184b090 100644
--- a/src/lib/Makefile.am
+++ b/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
diff --git a/src/lib/cryptolink/Makefile.am b/src/lib/cryptolink/Makefile.am
new file mode 100644
index 0000000..2f5d9c3
--- /dev/null
+++ b/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
diff --git a/src/lib/cryptolink/crypto_hmac.cc b/src/lib/cryptolink/crypto_hmac.cc
new file mode 100644
index 0000000..ea70c32
--- /dev/null
+++ b/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
diff --git a/src/lib/cryptolink/crypto_hmac.h b/src/lib/cryptolink/crypto_hmac.h
new file mode 100644
index 0000000..2eb0d0e
--- /dev/null
+++ b/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
+
diff --git a/src/lib/cryptolink/cryptolink.cc b/src/lib/cryptolink/cryptolink.cc
new file mode 100644
index 0000000..2847a5b
--- /dev/null
+++ b/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
+
diff --git a/src/lib/cryptolink/cryptolink.h b/src/lib/cryptolink/cryptolink.h
new file mode 100644
index 0000000..1583136
--- /dev/null
+++ b/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
diff --git a/src/lib/cryptolink/tests/Makefile.am b/src/lib/cryptolink/tests/Makefile.am
new file mode 100644
index 0000000..c8b5e26
--- /dev/null
+++ b/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)
diff --git a/src/lib/cryptolink/tests/crypto_unittests.cc b/src/lib/cryptolink/tests/crypto_unittests.cc
new file mode 100644
index 0000000..65018c6
--- /dev/null
+++ b/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);
+}
diff --git a/src/lib/cryptolink/tests/run_unittests.cc b/src/lib/cryptolink/tests/run_unittests.cc
new file mode 100644
index 0000000..d16327e
--- /dev/null
+++ b/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());
+}
diff --git a/src/lib/dns/python/tests/tsigkey_python_test.py b/src/lib/dns/python/tests/tsigkey_python_test.py
index 06e6868..97be501 100644
--- a/src/lib/dns/python/tests/tsigkey_python_test.py
+++ b/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'
diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc
index aa87909..906dfc0 100644
--- a/src/lib/dns/python/tsigkey_python.cc
+++ b/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) {
diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc
index 6b2b8c5..c10ffe0 100644
--- a/src/lib/dns/tests/tsigkey_unittest.cc
+++ b/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
diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc
index 057191d..e9b9162 100644
--- a/src/lib/dns/tsigkey.cc
+++ b/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");
diff --git a/src/lib/dns/tsigkey.h b/src/lib/dns/tsigkey.h
index e56fa88..ca9e9ec 100644
--- a/src/lib/dns/tsigkey.h
+++ b/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.
///
More information about the bind10-changes
mailing list