+794. [func] fdupont
+ cryptolink: add OpenSSL crypto backend as an alternative to Botan
+ by specifying --with-openssl[=PATH] on the "configure" command
+ line. Add hash support to the cryptolink API and use it in DHCP
+ DDNS, removing the Botan dependency.
+ (Trac #2406, git xxx)
+
793. [func] tmark
DHCP-DDNS: Implemented dynamic reconfiguration of the server,
triggered when the SIGHUP signal is received by the server's
LCOV=@LCOV@
GENHTML=@GENHTML@
DISTCHECK_GTEST_CONFIGURE_FLAG=@DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG=@DISTCHECK_CRYPTO_CONFIGURE_FLAG@
DISTCLEANFILES = config.report
# Use same --with-gtest flag if set
DISTCHECK_CONFIGURE_FLAGS += $(DISTCHECK_GTEST_CONFIGURE_FLAG)
+# Keep the crypto backend config
+DISTCHECK_CONFIGURE_FLAGS += $(DISTCHECK_CRYPTO_CONFIGURE_FLAG)
+
dist_doc_DATA = AUTHORS COPYING ChangeLog README
.PHONY: check-valgrind check-valgrind-suppress
c++/4.4\*/ext/\* \
c++/4.4\*/\*-\*/bits/\* \
boost/\* \
+if HAVE_BOTAN
botan/\* \
+endif
ext/asio/\* \
ext/coroutine/\* \
gtest/\* \
- log4cplus/\* \
include/\* \
+ log4cplus/\* \
+if HAVE_OPENSSL
+ openssl/\* \
+endif
tests/\* \
unittests/\* \
\*_unittests.cc \
IFS="$IFS_SAVED"
])
+# Avoid checking Botan if OpenSSL is wanted
+AC_ARG_WITH([openssl],
+ [AS_HELP_STRING([--with-openssl[[=PATH]]], [Enables OpenSSL,
+ location can be specified optionally])],
+ [use_openssl="$withval"],
+ [use_openssl="auto"])
+
# Botan helper test function
# Tries to compile a botan program, given the output of the given
# config tool
AC_MSG_CHECKING([usability of ${TOOL} ${TOOL_ARG}])
if test "$BOTAN_TOOL" != "" ; then
if test -x ${BOTAN_TOOL}; then
- BOTAN_LIBS=`$BOTAN_TOOL $TOOL_ARG --libs`
+ CRYPTO_LIBS=`$BOTAN_TOOL $TOOL_ARG --libs`
LIBS_SAVED=${LIBS}
- LIBS="$LIBS $BOTAN_LIBS"
- BOTAN_INCLUDES=`$BOTAN_TOOL $TOOL_ARG --cflags`
+ LIBS="$LIBS $CRYPTO_LIBS"
+ CRYPTO_INCLUDES=`$BOTAN_TOOL $TOOL_ARG --cflags`
CPPFLAGS_SAVED=${CPPFLAGS}
- CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS"
+ CPPFLAGS="$CRYPTO_INCLUDES $CPPFLAGS"
#AC_MSG_RESULT([found])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([#include <botan/botan.h>
# against botan should neither -config scripts nor pkgconfig data exist).
#
botan_config="yes"
+if test "${use_openssl}" != "auto" -a "${use_openssl}" != "no" ; then
+ botan_config="no"
+fi
AC_ARG_WITH([botan-config],
AC_HELP_STRING([--with-botan-config=PATH],
[specify the path to the botan-config script]),
[botan_config="$withval"])
if test "${botan_config}" = "no" ; then
- AC_MSG_ERROR([Need botan for libcryptolink])
-fi
-if test "${botan_config}" != "yes" ; then
+ if test "${use_openssl}" = "no" ; then
+ AC_MSG_ERROR([Need Botan or OpenSSL for libcryptolink])
+ fi
+elif test "${botan_config}" != "yes" ; then
if test -x "${botan_config}" ; then
if test -d "${botan_config}" ; then
AC_MSG_ERROR([${botan_config} is a directory])
if test "x${BOTAN_CONFIG}" != "x"
then
- BOTAN_LIBS=`${BOTAN_CONFIG} --libs`
- BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags`
+ CRYPTO_LIBS=`${BOTAN_CONFIG} --libs`
+ CRYPTO_INCLUDES=`${BOTAN_CONFIG} --cflags`
# We expect botan-config --libs to contain -L<path_to_libbotan>, but
# this is not always the case. As a heuristics workaround we add
# -L`botan-config --prefix/lib` in this case (if not present already).
- # Same for BOTAN_INCLUDES (but using include instead of lib) below.
+ # Same for CRYPTO_INCLUDES (but using include instead of lib) below.
if [ ${BOTAN_CONFIG} --prefix >/dev/null 2>&1 ] ; then
- echo ${BOTAN_LIBS} | grep -- -L > /dev/null || \
- BOTAN_LIBS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LIBS}"
- echo ${BOTAN_INCLUDES} | grep -- -I > /dev/null || \
- BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
+ echo ${CRYPTO_LIBS} | grep -- -L > /dev/null || \
+ CRYPTO_LIBS="-L`${BOTAN_CONFIG} --prefix`/lib ${CRYPTO_LIBS}"
+ echo ${CRYPTO_INCLUDES} | grep -- -I > /dev/null || \
+ CRYPTO_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${CRYPTO_INCLUDES}"
fi
fi
-dnl Determine the Botan version
-AC_MSG_CHECKING([Botan version])
-cat > conftest.cpp << EOF
+if test "x${CRYPTO_LIBS}" != "x"
+then
+ dnl Determine the Botan version
+ AC_MSG_CHECKING([Botan version])
+ cat > conftest.cpp << EOF
#include <botan/version.h>
AUTOCONF_BOTAN_VERSION=BOTAN_VERSION_MAJOR . BOTAN_VERSION_MINOR . BOTAN_VERSION_PATCH
EOF
-BOTAN_VERSION=`$CPP $CPPFLAGS $BOTAN_INCLUDES conftest.cpp | grep '^AUTOCONF_BOTAN_VERSION=' | $SED -e 's/^AUTOCONF_BOTAN_VERSION=//' -e 's/[[ ]]//g' -e 's/"//g' 2> /dev/null`
-if test -z "$BOTAN_VERSION"; then
- BOTAN_VERSION="unknown"
-fi
-$RM -f conftest.cpp
-AC_MSG_RESULT([$BOTAN_VERSION])
-
-# botan-config script (and the way we call pkg-config) returns -L and -l
-# as one string, but we need them in separate values
-BOTAN_LDFLAGS=
-BOTAN_NEWLIBS=
-for flag in ${BOTAN_LIBS}; do
- BOTAN_LDFLAGS="${BOTAN_LDFLAGS} `echo $flag | ${SED} -ne '/^\(\-L\)/p'`"
- BOTAN_LIBS="${BOTAN_LIBS} `echo $flag | ${SED} -ne '/^\(\-l\)/p'`"
-done
-
-# See python_rpath for some info on why we do this
-if test "x$ISC_RPATH_FLAG" != "x"; then
- BOTAN_RPATH=
- for flag in ${BOTAN_LIBS}; do
- BOTAN_RPATH="${BOTAN_RPATH} `echo $flag | ${SED} -ne "s/^\(\-L\)/${ISC_RPATH_FLAG}/p"`"
- done
-AC_SUBST(BOTAN_RPATH)
-
-# According to the libtool manual, it should be sufficient if we
-# specify the "-R libdir" in our wrapper library of botan (no other
-# programs will need libbotan directly); "libdir" should be added to
-# the program's binary image. But we've seen in our build environments
-# that (some versions of?) libtool doesn't propagate -R as documented,
-# and it caused a linker error at run time. To work around this, we
-# also add the rpath to the global LDFLAGS.
- LDFLAGS="$BOTAN_RPATH $LDFLAGS"
-fi
-
-AC_SUBST(BOTAN_LDFLAGS)
-AC_SUBST(BOTAN_LIBS)
-AC_SUBST(BOTAN_INCLUDES)
-# Even though chances are high we already performed a real compilation check
-# in the search for the right (pkg)config data, we try again here, to
-# be sure.
-CPPFLAGS_SAVED=$CPPFLAGS
-CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS"
-LIBS_SAVED="$LIBS"
-LIBS="$LIBS $BOTAN_LIBS"
-
-# ac_header_preproc is an autoconf symbol (undocumented but stable) that
-# is set if the pre-processor phase passes. Thus by adding a custom
-# failure handler we can detect the difference between a header not existing
-# (or not even passing the pre-processor phase) and a header file resulting
-# in compilation failures.
-AC_CHECK_HEADERS([botan/botan.h],,[
+ CRYPTO_VERSION=`$CPP $CPPFLAGS $CRYPTO_INCLUDES conftest.cpp | grep '^AUTOCONF_BOTAN_VERSION=' | $SED -e 's/^AUTOCONF_BOTAN_VERSION=//' -e 's/[[ ]]//g' -e 's/"//g' 2> /dev/null`
+ if test -z "$CRYPTO_VERSION"; then
+ CRYPTO_VERSION="unknown"
+ fi
+ $RM -f conftest.cpp
+ AC_MSG_RESULT([$CRYPTO_VERSION])
+
+ # botan-config script (and the way we call pkg-config) returns -L and -l
+ # as one string, but we need them in separate values
+ CRYPTO_LDFLAGS=
+ for flag in ${CRYPTO_LIBS}; do
+ CRYPTO_LDFLAGS="${CRYPTO_LDFLAGS} `echo $flag | ${SED} -ne '/^\(\-L\)/p'`"
+ CRYPTO_LIBS="${CRYPTO_LIBS} `echo $flag | ${SED} -ne '/^\(\-l\)/p'`"
+ done
+
+ # See crypto_rpath for some info on why we do this
+ if test "x$ISC_RPATH_FLAG" != "x"; then
+ CRYPTO_RPATH=
+ for flag in ${CRYPTO_LIBS}; do
+ CRYPTO_RPATH="${CRYPTO_RPATH} `echo $flag | ${SED} -ne "s/^\(\-L\)/${ISC_RPATH_FLAG}/p"`"
+ done
+ # According to the libtool manual, it should be sufficient if we
+ # specify the "-R libdir" in our wrapper library of botan (no other
+ # programs will need libbotan directly); "libdir" should be added to
+ # the program's binary image. But we've seen in our build environments
+ # that (some versions of?) libtool doesn't propagate -R as documented,
+ # and it caused a linker error at run time. To work around this, we
+ # also add the rpath to the global LDFLAGS.
+ LDFLAGS="$CRYPTO_RPATH $LDFLAGS"
+ fi
+
+ # Even though chances are high we already performed a real compilation check
+ # in the search for the right (pkg)config data, we try again here, to
+ # be sure.
+ CPPFLAGS_SAVED=$CPPFLAGS
+ CPPFLAGS="$CRYPTO_INCLUDES $CPPFLAGS"
+ LIBS_SAVED="$LIBS"
+ LIBS="$LIBS $CRYPTO_LIBS"
+
+ # ac_header_preproc is an autoconf symbol (undocumented but stable) that
+ # is set if the pre-processor phase passes. Thus by adding a custom
+ # failure handler we can detect the difference between a header not existing
+ # (or not even passing the pre-processor phase) and a header file resulting
+ # in compilation failures.
+ AC_CHECK_HEADERS([botan/botan.h],,[
+ CRYPTO_INCLUDES=""
+ CRYPTO_LIBS=""
+ CRYPTO_LDFLAGS=""
+ CRYPTO_RPATH=""
if test "x$ac_header_preproc" = "xyes"; then
- AC_MSG_ERROR([
+ AC_MSG_RESULT([
botan/botan.h was found but is unusable. The most common cause of this problem
is attempting to use an updated C++ compiler with older C++ libraries, such as
the version of Botan that comes with your distribution. If you have updated
your C++ compiler we highly recommend that you use support libraries such as
Boost and Botan that were compiled with the same compiler version.])
else
- AC_MSG_ERROR([Missing required header files.])
+ AC_MSG_RESULT([Missing required header files.])
fi]
-)
-AC_LINK_IFELSE(
+ )
+ CPPFLAGS=$CPPFLAGS_SAVED
+ LIBS=$LIBS_SAVED
+fi
+
+if test "x${CRYPTO_LIBS}" != "x"
+then
+ CPPFLAGS_SAVED=$CPPFLAGS
+ CPPFLAGS="$CRYPTO_INCLUDES $CPPFLAGS"
+ LIBS_SAVED="$LIBS"
+ LIBS="$LIBS $CRYPTO_LIBS"
+ AC_LINK_IFELSE(
[AC_LANG_PROGRAM([#include <botan/botan.h>
#include <botan/hash.h>
],
])],
[AC_MSG_RESULT([checking for Botan library... yes])],
[AC_MSG_RESULT([checking for Botan library... no])
- AC_MSG_ERROR([Needs Botan library 1.8 or higher. On some systems,
+ CRYPTO_INCLUDES=""
+ CRYPTO_LIBS=""
+ CRYPTO_LDFLAGS=""
+ CRYPTO_RPATH=""
+ AC_MSG_RESULT([Needs Botan library 1.8 or higher. On some systems,
the botan package has a few missing dependencies (libbz2 and
- libgmp), if libbotan has been installed and you see this error,
+ libgmp), if libbotan has been installed and you see this message,
try upgrading to a higher version of botan or installing libbz2
and libgmp.])]
-)
-CPPFLAGS=$CPPFLAGS_SAVED
-LIBS=$LIBS_SAVED
+ )
+ CPPFLAGS=$CPPFLAGS_SAVED
+ LIBS=$LIBS_SAVED
+fi
+
+if test "x${CRYPTO_LIBS}" != "x"
+then
+ CRYPTO_NAME="Botan"
+ DISABLED_CRYPTO="OpenSSL"
+ CRYPTO_PACKAGE="botan-1.8"
+ CRYPTO_CFLAGS=""
+ DISTCHECK_CRYPTO_CONFIGURE_FLAG="--with-botan=$botan_config"
+ AC_DEFINE_UNQUOTED([WITH_BOTAN], [], [Compile with Botan crypto])
+else
+ CRYPTO_NAME="OpenSSL"
+ DISABLED_CRYPTO="Botan"
+ CRYPTO_PACKAGE="openssl-1.0.0"
+ AC_DEFINE_UNQUOTED([WITH_OPENSSL], [], [Compile with OpenSSL crypto])
+ AC_MSG_CHECKING(for OpenSSL library)
+ # from bind9
+
+ if test "${use_openssl}" = "auto" ; then
+ use_openssl="yes"
+ fi
+ if test "${use_openssl}" = "yes" ; then
+ for d in /usr /usr/local /usr/local/ssl /usr/pkg /usr/sfw; do
+ if test -f $d/include/openssl/opensslv.h; then
+ use_openssl=$d; break
+ fi
+ done
+ fi
+ if test "${use_openssl}" = "yes" ; then
+ AC_MSG_ERROR([OpenSSL auto detection failed])
+ fi
+ if ! test -f "${use_openssl}"/include/openssl/opensslv.h ; then
+ AC_MSG_ERROR([OpenSSL not found at ${use_openssl}])
+ fi
+ AC_MSG_RESULT(yes)
+ if test "${use_openssl}" = "/usr" ; then
+ CRYPTO_INCLUDES=""
+ CRYPTO_LIBS="-lcrypto"
+ DISTCHECK_CRYPTO_CONFIGURE_FLAG="--with-openssl"
+ case "$host" in
+ *-apple-darwin*)
+ # Starting with OSX 10.7 (Lion) OpenSSL is deprecated
+ CRYPTO_CFLAGS="-Wno-deprecated-declarations"
+ ;;
+ *)
+ CRYPTO_CFLAGS=""
+ ;;
+ esac
+ else
+ CRYPTO_CFLAGS=""
+ CRYPTO_INCLUDES="-I${use_openssl}/include"
+ DISTCHECK_CRYPTO_CONFIGURE_FLAG="--with-openssl=${use_openssl}"
+ case $host in
+ *-solaris*)
+ CRYPTO_LIBS="-L${use_openssl}/lib -R${use_openssl}/lib -lcrypto"
+ ;;
+ *-hp-hpux*)
+ CRYPTO_LIBS="-L${use_openssl}/lib -Wl,+b: -lcrypto"
+ ;;
+ *-apple-darwin*)
+ if test -f "${use_openssl}/lib/libcrypto.dylib" ; then
+ CRYPTO_LIBS="-L${use_openssl}/lib -lcrypto"
+ else
+ CRYPTO_LIBS="${use_openssl}/lib/libcrypto.a"
+ fi
+ ;;
+ *)
+ CRYPTO_LIBS="-L${use_openssl}/lib -lcrypto"
+ ;;
+ esac
+ fi
+ dnl Determine the OpenSSL version
+ AC_MSG_CHECKING([OpenSSL version])
+ cat > conftest.cpp << EOF
+#include <openssl/opensslv.h>
+AUTOCONF_OPENSSL_VERSION=OPENSSL_VERSION_TEXT
+EOF
+
+ CRYPTO_VERSION=`$CPP $CPPFLAGS $CRYPTO_INCLUDES conftest.cpp | grep '^AUTOCONF_OPENSSL_VERSION=' | $SED -e 's/^AUTOCONF_OPENSSL_VERSION=//' -e 's/"//g' 2> /dev/null`
+ if test -z "$CRYPTO_VERSION" ; then
+ CRYPTO_VERSION="unknown"
+ fi
+ $RM -f conftest.cpp
+ AC_MSG_RESULT([$CRYPTO_VERSION])
+ #CRYPTO_LDFLAGS="-ldl"
+ CRYPTO_LDFLAGS=""
+ CRYPTO_RPATH=""
+fi
+
+AM_CONDITIONAL(HAVE_BOTAN, test "$CRYPTO_NAME" = "Botan")
+AM_CONDITIONAL(HAVE_OPENSSL, test "$CRYPTO_NAME" = "OpenSSL")
+AC_SUBST(CRYPTO_INCLUDES)
+AC_SUBST(CRYPTO_CFLAGS)
+AC_SUBST(CRYPTO_LIBS)
+AC_SUBST(CRYPTO_LDFLAGS)
+AC_SUBST(CRYPTO_PACKAGE)
+AC_SUBST(CRYPTO_RPATH)
+AC_SUBST(DISTCHECK_CRYPTO_CONFIGURE_FLAG)
# Check for MySql. The path to the mysql_config program is given with
# the --with-mysql-config (default to /usr/bin/mysql-config). By default,
BOOST_VERSION: ${BOOST_VERSION}
BOOST_INCLUDES: ${BOOST_INCLUDES}
-Botan:
- BOTAN_VERSION: ${BOTAN_VERSION}
- BOTAN_INCLUDES: ${BOTAN_INCLUDES}
- BOTAN_LDFLAGS: ${BOTAN_LDFLAGS}
- BOTAN_LIBS: ${BOTAN_LIBS}
+${CRYPTO_NAME}:
+ CRYPTO_VERSION: ${CRYPTO_VERSION}
+ CRYPTO_CFLAGS: ${CRYPTO_CFLAGS}
+ CRYPTO_INCLUDES: ${CRYPTO_INCLUDES}
+ CRYPTO_LDFLAGS: ${CRYPTO_LDFLAGS}
+ CRYPTO_LIBS: ${CRYPTO_LIBS}
+
+${DISABLED_CRYPTO}: no
Log4cplus:
LOG4CPLUS_VERSION: ${LOG4CPLUS_VERSION}
Name: dns++
Description: BIND 10 DNS library
Version: @PACKAGE_VERSION@
-Requires: botan-1.8
+Requires: @CRYPTO_PACKAGE@
Cflags: -I${includedir}/@PACKAGE_NAME@
Libs: -L${libdir} -lb10-dns++ -lb10-cryptolink -lb10-util -lb10-exceptions -lm
</para>
<para>
- Kea uses the Botan crypto library for C++
- (<ulink url="http://botan.randombit.net/"/>).
- It requires at least Botan version 1.8.
+ Kea supports two crypto libraries: Botan and OpenSSL. Only one
+ of them is required during compilation. Kea uses the Botan crypto
+ library for C++ (<ulink url="http://botan.randombit.net/"/>).
+ It requires at least Botan version 1.8. As an alternative to Botan,
+ Kea can use the OpenSSL crypto library
+ (<ulink url="http://www.openssl.org/"/>).
+ It requires a version with SHA-2 support.
</para>
<para>
<para>
To build Kea, also install the Botan (at least version
- 1.8) and the log4cplus (at least version 1.0.3)
+ 1.8) or OpenSSL, and the log4cplus (at least version 1.0.3)
development include headers.
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>--with-openssl</term>
+ <listitem>
+ <simpara>Replace Botan by OpenSSL for the crypto library.
+ The default is to try to find a working Botan then
+ OpenSSL only if not found.
+<!-- missing -with-botan-config -->
+ </simpara>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term>--without-werror</term>
<listitem>
$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
chmod a+x $@
-b10_certgen_SOURCES = b10-certgen.cc
-b10_certgen_CXXFLAGS = $(BOTAN_INCLUDES)
-b10_certgen_LDFLAGS = $(BOTAN_LIBS)
+if HAVE_BOTAN
+b10_certgen_SOURCES = botan-certgen.cc
+EXTRA_DIST += openssl-certgen.cc
+endif
+if HAVE_OPENSSL
+b10_certgen_SOURCES = openssl-certgen.cc
+EXTRA_DIST += botan-certgen.cc
+endif
+b10_certgen_CXXFLAGS = $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
+b10_certgen_LDFLAGS = $(CRYPTO_LIBS)
# Generate the initial certificates immediately
cmdctl-keyfile.pem: b10-certgen
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include <cstring>
+#include <iostream>
+#include <fstream>
+#include <memory>
+#include <getopt.h>
+
+// For cleaner 'does not exist or is not readable' output than
+// openssl provides
+#include <unistd.h>
+#include <errno.h>
+
+// This is a simple tool that creates a self-signed PEM certificate
+// for use with BIND 10. It creates a simple certificate for initial
+// setup. Currently, all values are hardcoded defaults. For future
+// versions, we may want to add more options for administrators.
+
+// It will create a PEM file containing a certificate with the following
+// values:
+// common name: localhost
+// organization: BIND10
+// country code: US
+
+// Additional error return codes; these are specifically
+// chosen to be distinct from validation error codes as
+// provided by OpenSSL. Their main use is to distinguish
+// error cases in the unit tests.
+const int DECODING_ERROR = 100;
+const int BAD_OPTIONS = 101;
+const int READ_ERROR = 102;
+const int WRITE_ERROR = 103;
+const int UNKNOWN_ERROR = 104;
+const int NO_SUCH_FILE = 105;
+const int FILE_PERMISSION_ERROR = 106;
+
+void
+usage() {
+ std::cout << "Usage: b10-certgen [OPTION]..." << std::endl;
+ std::cout << "Validate, create, or update a self-signed certificate for "
+ "use with b10-cmdctl" << std::endl;
+ std::cout << "" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << "-c, --certfile=FILE\t\tfile to read or store the certificate"
+ << std::endl;
+ std::cout << "-f, --force\t\t\toverwrite existing certificate even if it"
+ << std::endl <<"\t\t\t\tis valid" << std::endl;
+ std::cout << "-h, --help\t\t\tshow this help" << std::endl;
+ std::cout << "-k, --keyfile=FILE\t\tfile to store the generated private key"
+ << std::endl;
+ std::cout << "-w, --write\t\t\tcreate a new certificate if the given file"
+ << std::endl << "\t\t\t\tdoes not exist, or if is is not valid"
+ << std::endl;
+ std::cout << "-q, --quiet\t\t\tprint no output when creating or validating"
+ << std::endl;
+}
+
+/// \brief Returns true if the given file exists
+///
+/// \param filename The file to check
+/// \return true if file exists
+bool
+fileExists(const std::string& filename) {
+ return (access(filename.c_str(), F_OK) == 0);
+}
+
+/// \brief Returns true if the given file exists and is readable
+///
+/// \param filename The file to check
+/// \return true if file exists and is readable
+bool
+fileIsReadable(const std::string& filename) {
+ return (access(filename.c_str(), R_OK) == 0);
+}
+
+/// \brief Returns true if the given file exists and is writable
+///
+/// \param filename The file to check
+/// \return true if file exists and is writable
+bool
+fileIsWritable(const std::string& filename) {
+ return (access(filename.c_str(), W_OK) == 0);
+}
+
+class CertificateTool {
+public:
+ CertificateTool(bool quiet) : quiet_(quiet) {}
+
+ int
+ createKeyAndCertificate(const std::string& key_file_name,
+ const std::string& cert_file_name) {
+ // Create and store a private key
+ print("Creating key file " + key_file_name);
+ RSA* rsa = RSA_generate_key(2048, 65537UL, NULL, NULL);
+ std::ofstream key_file(key_file_name.c_str());
+ if (!key_file.good()) {
+ print(std::string("Error writing to ") + key_file_name +
+ ": " + std::strerror(errno));
+ return (WRITE_ERROR);
+ }
+ BIO* key_mem = BIO_new(BIO_s_mem());
+ PEM_write_bio_RSAPrivateKey(key_mem, rsa, NULL, NULL, 0, NULL, NULL);
+ char* p;
+ long len = BIO_get_mem_data(key_mem, &p);
+ key_file.write(p, (unsigned) len);
+ BIO_free(key_mem);
+ if (!key_file.good()) {
+ print(std::string("Error writing to ") + key_file_name +
+ ": " + std::strerror(errno));
+ return (WRITE_ERROR);
+ }
+ key_file.close();
+
+ // Certificate options, currently hardcoded.
+ // For a future version we may want to make these
+ // settable.
+ X509* cert = X509_new();
+ X509_set_version(cert, 2);
+ BIGNUM* serial = BN_new();
+ BN_pseudo_rand(serial, 64, 0, 0);
+ BN_to_ASN1_INTEGER(serial, X509_get_serialNumber(cert));
+ BN_free(serial);
+ X509_NAME* name = X509_get_subject_name(cert);
+ std::string cn("localhost");
+ X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
+ (unsigned char*) cn.c_str(), cn.size(),
+ -1, 0);
+ std::string org("UNKNOWN");
+ X509_NAME_add_entry_by_NID(name, NID_organizationName, MBSTRING_ASC,
+ (unsigned char*) org.c_str(), org.size(),
+ -1, 0);
+ std::string cc("XX");
+ X509_NAME_add_entry_by_NID(name, NID_countryName, MBSTRING_ASC,
+ (unsigned char*) cc.c_str(), cc.size(),
+ -1, 0);
+ X509_set_issuer_name(cert, name);
+ X509_gmtime_adj(X509_get_notBefore(cert), 0);
+ X509_gmtime_adj(X509_get_notAfter(cert), 60*60*24*365L);
+ EVP_PKEY* pkey = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(pkey, rsa);
+ X509_set_pubkey(cert, pkey);
+ X509V3_CTX ec;
+ X509V3_set_ctx_nodb(&ec);
+ X509V3_set_ctx(&ec, cert, cert, NULL, NULL, 0);
+ const std::string bc_val("critical,CA:TRUE,pathlen:1");
+ X509_EXTENSION* bc = X509V3_EXT_conf_nid(NULL, &ec,
+ NID_basic_constraints,
+ (char*) bc_val.c_str());
+ X509_add_ext(cert, bc, -1);
+ X509_EXTENSION_free(bc);
+ const std::string ku_val=("critical,keyCertSign,cRLSign");
+ X509_EXTENSION* ku = X509V3_EXT_conf_nid(NULL, &ec,
+ NID_key_usage,
+ (char*) ku_val.c_str());
+ X509_add_ext(cert, ku, -1);
+ X509_EXTENSION_free(ku);
+ const std::string ski_val("hash");
+ X509_EXTENSION* ski = X509V3_EXT_conf_nid(NULL, &ec,
+ NID_subject_key_identifier,
+ (char*) ski_val.c_str());
+ X509_add_ext(cert, ski, -1);
+ X509_EXTENSION_free(ski);
+ X509_sign(cert, pkey, EVP_sha256());
+
+ print("Creating certificate file " + cert_file_name);
+ std::ofstream cert_file(cert_file_name.c_str());
+ if (!cert_file.good()) {
+ print(std::string("Error writing to ") + cert_file_name +
+ ": " + std::strerror(errno));
+ return (WRITE_ERROR);
+ }
+ BIO* cert_mem = BIO_new(BIO_s_mem());
+ PEM_write_bio_X509(cert_mem, cert);
+ p = NULL;
+ len = BIO_get_mem_data(cert_mem, &p);
+ cert_file.write(p, (unsigned) len);
+ BIO_free(cert_mem);
+ if (!cert_file.good()) {
+ print(std::string("Error writing to ") + cert_file_name +
+ ": " + std::strerror(errno));
+ return (WRITE_ERROR);
+ }
+ cert_file.close();
+ X509_free(cert);
+ RSA_free(rsa);
+ return (0);
+ }
+
+ int
+ validateCertificate(const std::string& certfile) {
+ // Since we are dealing with a self-signed certificate here, we
+ // also use the certificate to check itself; i.e. we add it
+ // as a trusted certificate, then validate the certificate itself.
+ BIO* in = BIO_new_file(certfile.c_str(), "r");
+ if (in == NULL) {
+ print("failed to read " + certfile);
+ return (READ_ERROR);
+ }
+ X509* cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
+ BIO_free(in);
+ if (cert == NULL) {
+ print("failed to decode " + certfile);
+ return (DECODING_ERROR);
+ }
+ X509_STORE* store = X509_STORE_new();
+ X509_STORE_CTX* csc = X509_STORE_CTX_new();
+ X509_STORE_CTX_init(csc, store, cert, NULL);
+ STACK_OF(X509)* trusted = sk_X509_new_null();
+ sk_X509_push(trusted, X509_dup(cert));
+ X509_STORE_CTX_trusted_stack(csc, trusted);
+ const int result = X509_verify_cert(csc);
+ const int cerror = X509_STORE_CTX_get_error(csc);
+ X509_STORE_CTX_free(csc);
+ X509_free(cert);
+
+ if (result > 0) {
+ print(certfile + " is valid");
+ return (X509_V_OK);
+ } else {
+ print(certfile + " failed to verify: " +
+ X509_verify_cert_error_string(cerror));
+ return (cerror);
+ }
+ }
+
+ /// \brief Runs the tool
+ ///
+ /// \param create_cert Create certificate if true, validate if false.
+ /// Does nothing if certificate exists and is valid.
+ /// \param force_create Create new certificate even if it is valid.
+ /// \param certfile Certificate file to read to or write from.
+ /// \param keyfile Key file to write if certificate is created.
+ /// Ignored if create_cert is false
+ /// \return zero on success, non-zero on failure
+ int
+ run(bool create_cert, bool force_create, const std::string& certfile,
+ const std::string& keyfile)
+ {
+ if (create_cert) {
+ // Unless force is given, only create it if the current
+ // one is not OK
+
+ // First do some basic permission checks; both files
+ // should either not exist, or be both readable
+ // and writable
+ // The checks are done one by one so all errors can
+ // be enumerated in one go
+ if (fileExists(certfile)) {
+ if (!fileIsReadable(certfile)) {
+ print(certfile + " not readable: " + std::strerror(errno));
+ create_cert = false;
+ }
+ if (!fileIsWritable(certfile)) {
+ print(certfile + " not writable: " + std::strerror(errno));
+ create_cert = false;
+ }
+ }
+ // The key file really only needs write permissions (for
+ // b10-certgen that is)
+ if (fileExists(keyfile)) {
+ if (!fileIsWritable(keyfile)) {
+ print(keyfile + " not writable: " + std::strerror(errno));
+ create_cert = false;
+ }
+ }
+ if (!create_cert) {
+ print("Not creating new certificate, "
+ "check file permissions");
+ return (FILE_PERMISSION_ERROR);
+ }
+
+ // If we reach this, we know that if they exist, we can both
+ // read and write them, so now it's up to content checking
+ // and/or force_create
+
+ if (force_create || !fileExists(certfile) ||
+ validateCertificate(certfile) != X509_V_OK) {
+ return (createKeyAndCertificate(keyfile, certfile));
+ } else {
+ print("Not creating a new certificate (use -f to force)");
+ }
+ } else {
+ if (!fileExists(certfile)) {
+ print(certfile + ": " + std::strerror(errno));
+ return (NO_SUCH_FILE);
+ }
+ if (!fileIsReadable(certfile)) {
+ print(certfile + " not readable: " + std::strerror(errno));
+ return (FILE_PERMISSION_ERROR);
+ }
+ int result = validateCertificate(certfile);
+ if (result != 0) {
+ print("Running with -w would overwrite the certificate");
+ }
+ return (result);
+ }
+ return (0);
+ }
+private:
+ /// Prints the message to stdout unless quiet_ is true
+ void print(const std::string& msg) {
+ if (!quiet_) {
+ std::cout << msg << std::endl;
+ }
+ }
+
+ bool quiet_;
+};
+
+int
+main(int argc, char* argv[])
+{
+ // ERR_load_crypto_strings();
+
+ // create or check certificate
+ bool create_cert = false;
+ // force creation even if not necessary
+ bool force_create = false;
+ // don't print any output
+ bool quiet = false;
+
+ // default certificate file
+ std::string certfile("cmdctl-certfile.pem");
+ // default key file
+ std::string keyfile("cmdctl-keyfile.pem");
+
+ // whether or not the above values have been
+ // overridden (used in command line checking)
+ bool certfile_default = true;
+ bool keyfile_default = true;
+
+ // It would appear some environments insist on
+ // char* here (Sunstudio on Solaris), so we const_cast
+ // them to get rid of compiler warnings.
+ const struct option long_options[] = {
+ { const_cast<char*>("certfile"), required_argument, NULL, 'c' },
+ { const_cast<char*>("force"), no_argument, NULL, 'f' },
+ { const_cast<char*>("help"), no_argument, NULL, 'h' },
+ { const_cast<char*>("keyfile"), required_argument, NULL, 'k' },
+ { const_cast<char*>("write"), no_argument, NULL, 'w' },
+ { const_cast<char*>("quiet"), no_argument, NULL, 'q' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt, option_index;
+ while ((opt = getopt_long(argc, argv, "c:fhk:wq", long_options,
+ &option_index)) != -1) {
+ switch (opt) {
+ case 'c':
+ certfile = optarg;
+ certfile_default = false;
+ break;
+ case 'f':
+ force_create = true;
+ break;
+ case 'h':
+ usage();
+ return (0);
+ break;
+ case 'k':
+ keyfile = optarg;
+ keyfile_default = false;
+ break;
+ case 'w':
+ create_cert = true;
+ break;
+ case 'q':
+ quiet = true;
+ break;
+ default:
+ // A message will have already been output about the error.
+ return (BAD_OPTIONS);
+ }
+ }
+
+ if (optind < argc) {
+ std::cout << "Error: extraneous arguments" << std::endl << std::endl;
+ usage();
+ return (BAD_OPTIONS);
+ }
+
+ // Some sanity checks on option combinations
+ if (create_cert && (certfile_default ^ keyfile_default)) {
+ std::cout << "Error: keyfile and certfile must both be specified "
+ "if one of them is when calling b10-certgen in write "
+ "mode." << std::endl;
+ return (BAD_OPTIONS);
+ }
+ if (!create_cert && !keyfile_default) {
+ std::cout << "Error: keyfile is not used when not in write mode"
+ << std::endl;
+ return (BAD_OPTIONS);
+ }
+
+ // Initialize the tool and perform the appropriate action(s)
+ CertificateTool tool(quiet);
+ return (tool.run(create_cert, force_create, certfile, keyfile));
+}
PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
-PYTESTS = cmdctl_test.py b10-certgen_test.py
-EXTRA_DIST = $(PYTESTS)
+PYTESTS = cmdctl_test.py
+
+EXTRA_DIST =
+if HAVE_BOTAN
+PYTESTS += botan-certgen_test.py
+EXTRA_DIST += openssl-certgen_test.py
+endif
+if HAVE_OPENSSL
+EXTRA_DIST += botan-certgen_test.py
+PYTESTS += openssl-certgen_test.py
+endif
+EXTRA_DIST += $(PYTESTS)
EXTRA_DIST += testdata/expired-certfile.pem
EXTRA_DIST += testdata/mangled-certfile.pem
EXTRA_DIST += testdata/noca-certfile.pem
--- /dev/null
+# Copyright (C) 2014 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and 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 INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM 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.
+
+# Note: the main code is in C++, but what we are mostly testing is
+# options and behaviour (output/file creation, etc), which is easier
+# to test in python.
+
+import unittest
+import os
+from subprocess import call
+import subprocess
+import ssl
+import stat
+
+def run(command):
+ """
+ Small helper function that returns a tuple of (rcode, stdout, stderr) after
+ running the given command (an array of command and arguments, as passed on
+ to subprocess).
+ """
+ subp = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = subp.communicate()
+ return (subp.returncode, stdout, stderr)
+
+class FileDeleterContext:
+ """
+ Simple Context Manager that deletes a given set of files when the context
+ is left.
+ """
+ def __init__(self, files):
+ self.files = files
+
+ def __enter__(self):
+ pass
+
+ def __exit__(self, type, value, traceback):
+ for f in self.files:
+ if os.path.exists(f):
+ os.unlink(f)
+
+class FilePermissionContext:
+ """
+ Simple Context Manager that temporarily modifies file permissions for
+ a given file
+ """
+ def __init__(self, f, unset_flags = [], set_flags = []):
+ """
+ Initialize file permission context.
+ See the stat module for possible flags to set or unset.
+ The flags are changed when the context is entered (i.e.
+ you can create the context first without any change)
+ The flags are changed back when the context is left.
+
+ Parameters:
+ f: string, file to change permissions for
+ unset_flags: list of flags to unset
+ set_flags: list of flags to set
+ """
+ self.file = f
+ self.orig_mode = os.stat(f).st_mode
+ new_mode = self.orig_mode
+ for flag in unset_flags:
+ new_mode = new_mode & ~flag
+ for flag in set_flags:
+ new_mode = new_mode | flag
+ self.new_mode = new_mode
+
+ def __enter__(self):
+ os.chmod(self.file, self.new_mode)
+
+ def __exit__(self, type, value, traceback):
+ os.chmod(self.file, self.orig_mode)
+
+def read_file_data(filename):
+ """
+ Simple text file reader that returns its contents as an array
+ """
+ with open(filename) as f:
+ return f.readlines()
+
+class TestCertGenTool(unittest.TestCase):
+ TOOL = '../b10-certgen'
+
+ def run_check(self, expected_returncode, expected_stdout, expected_stderr, command):
+ """
+ Runs the given command, and checks return code, and outputs (if provided).
+ Arguments:
+ expected_returncode, return code of the command
+ expected_stdout, (multiline) string that is checked agains stdout.
+ May be None, in which case the check is skipped.
+ expected_stderr, (multiline) string that is checked agains stderr.
+ May be None, in which case the check is skipped.
+ """
+ (returncode, stdout, stderr) = run(command)
+ self.assertEqual(expected_returncode, returncode, " ".join(command))
+ if expected_stdout is not None:
+ self.assertEqual(expected_stdout, stdout.decode())
+ if expected_stderr is not None:
+ self.assertEqual(expected_stderr, stderr.decode())
+
+ def validate_certificate(self, expected_result, certfile):
+ """
+ Validate a certificate, using the quiet option of the tool; it runs
+ the check option (-c) for the given base name of the certificate (-f
+ <certfile>), and compares the return code to the given
+ expected_result value
+ """
+ self.run_check(expected_result, '', '',
+ [self.TOOL, '-q', '-c', certfile])
+ # Same with long options
+ self.run_check(expected_result, '', '',
+ [self.TOOL, '--quiet', '--certfile', certfile])
+
+
+ def test_basic_creation(self):
+ """
+ Tests whether basic creation with no arguments (except output
+ file name) successfully creates a key and certificate
+ """
+ keyfile = 'test-keyfile.pem'
+ certfile = 'test-certfile.pem'
+ command = [ self.TOOL, '-q', '-w', '-c', certfile, '-k', keyfile ]
+ self.creation_helper(command, certfile, keyfile)
+ # Do same with long options
+ command = [ self.TOOL, '--quiet', '--write', '--certfile=' + certfile, '--keyfile=' + keyfile ]
+ self.creation_helper(command, certfile, keyfile)
+
+ def creation_helper(self, command, certfile, keyfile):
+ """
+ Helper method for test_basic_creation.
+ Performs the actual checks
+ """
+ with FileDeleterContext([keyfile, certfile]):
+ self.assertFalse(os.path.exists(keyfile))
+ self.assertFalse(os.path.exists(certfile))
+ self.run_check(0, '', '', command)
+ self.assertTrue(os.path.exists(keyfile))
+ self.assertTrue(os.path.exists(certfile))
+
+ # Validate the certificate that was just created
+ self.validate_certificate(0, certfile)
+
+ # When run with the same options, it should *not* create it again,
+ # as the current certificate should still be valid
+ certdata = read_file_data(certfile)
+ keydata = read_file_data(keyfile)
+
+ self.run_check(0, '', '', command)
+
+ self.assertEqual(certdata, read_file_data(certfile))
+ self.assertEqual(keydata, read_file_data(keyfile))
+
+ # but if we add -f, it should force a new creation
+ command.append('-f')
+ self.run_check(0, '', '', command)
+ self.assertNotEqual(certdata, read_file_data(certfile))
+ self.assertNotEqual(keydata, read_file_data(keyfile))
+
+ def test_check_bad_certificates(self):
+ """
+ Tests a few pre-created certificates with the -c option
+ """
+ path = os.environ['CMDCTL_SRC_PATH'] + '/tests/testdata/'
+ self.validate_certificate(10, path + 'expired-certfile.pem')
+ self.validate_certificate(100, path + 'mangled-certfile.pem')
+ self.validate_certificate(20, path + 'noca-certfile.pem')
+
+ def test_bad_options(self):
+ """
+ Tests some combinations of commands that should fail.
+ """
+ # specify -c but not -k
+ self.run_check(101,
+ 'Error: keyfile and certfile must both be specified '
+ 'if one of them is when calling b10-certgen in write '
+ 'mode.\n',
+ '', [self.TOOL, '-w', '-c', 'foo'])
+ self.run_check(101,
+ 'Error: keyfile and certfile must both be specified '
+ 'if one of them is when calling b10-certgen in write '
+ 'mode.\n',
+ '', [self.TOOL, '-w', '-k', 'foo'])
+ self.run_check(101,
+ 'Error: keyfile is not used when not in write mode\n',
+ '', [self.TOOL, '-k', 'foo'])
+ # Extraneous argument
+ self.run_check(101, None, None, [self.TOOL, 'foo'])
+ # No such file
+ self.run_check(105, None, None, [self.TOOL, '-c', 'foo'])
+
+ @unittest.skipIf(os.getuid() == 0,
+ 'test cannot be run as root user')
+ def test_permissions(self):
+ """
+ Test some combinations of correct and bad permissions.
+ """
+ keyfile = 'mod-keyfile.pem'
+ certfile = 'mod-certfile.pem'
+ command = [ self.TOOL, '-q', '-w', '-c', certfile, '-k', keyfile ]
+ # Delete them at the end
+ with FileDeleterContext([keyfile, certfile]):
+ # Create the two files first
+ self.run_check(0, '', '', command)
+ self.validate_certificate(0, certfile)
+
+ # Make the key file unwritable
+ with FilePermissionContext(keyfile, unset_flags = [stat.S_IWUSR]):
+ self.run_check(106, '', '', command)
+ # Should have no effect on validation
+ self.validate_certificate(0, certfile)
+
+ # Make the cert file unwritable
+ with FilePermissionContext(certfile, unset_flags = [stat.S_IWUSR]):
+ self.run_check(106, '', '', command)
+ # Should have no effect on validation
+ self.validate_certificate(0, certfile)
+
+ # Make the key file unreadable (this should not matter)
+ with FilePermissionContext(keyfile, unset_flags = [stat.S_IRUSR]):
+ self.run_check(0, '', '', command)
+
+ # unreadable key file should also not have any effect on
+ # validation
+ self.validate_certificate(0, certfile)
+
+ # Make the cert file unreadable (this should matter)
+ with FilePermissionContext(certfile, unset_flags = [stat.S_IRUSR]):
+ self.run_check(106, '', '', command)
+
+ # Unreadable cert file should also fail validation
+ self.validate_certificate(106, certfile)
+
+ # Not directly a permission problem, but trying to check or create
+ # in a nonexistent directory returns different error codes
+ self.validate_certificate(105, 'fakedir/cert')
+ self.run_check(103, '', '', [ self.TOOL, '-q', '-w', '-c',
+ 'fakedir/cert', '-k', 'fakedir/key' ])
+
+if __name__== '__main__':
+ unittest.main()
+
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/user_chk -I$(top_srcdir)/src/hooks/dhcp/user_chk
-AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_srcdir)/src/hooks/dhcp/user_chk/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
-libdhcp_user_chk_unittests_LDADD += ${BOTAN_LIBS} ${BOTAN_RPATH}
+libdhcp_user_chk_unittests_LDADD += ${CRYPTO_LIBS} ${CRYPTO_RPATH}
libdhcp_user_chk_unittests_LDADD += $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libkea-cryptolink.la
libkea_cryptolink_la_SOURCES = cryptolink.h cryptolink.cc
+libkea_cryptolink_la_SOURCES += crypto_hash.h crypto_hash.cc
libkea_cryptolink_la_SOURCES += crypto_hmac.h crypto_hmac.cc
+if HAVE_BOTAN
+libkea_cryptolink_la_SOURCES += botan_link.cc
+libkea_cryptolink_la_SOURCES += botan_hash.cc
+libkea_cryptolink_la_SOURCES += botan_hmac.cc
+endif
+if HAVE_OPENSSL
+libkea_cryptolink_la_SOURCES += openssl_link.cc
+libkea_cryptolink_la_SOURCES += openssl_hash.cc
+libkea_cryptolink_la_SOURCES += openssl_hmac.cc
+endif
-libkea_cryptolink_la_LDFLAGS = ${BOTAN_LDFLAGS}
-libkea_cryptolink_la_LIBADD = ${BOTAN_LIBS} ${BOTAN_RPATH}
+libkea_cryptolink_la_LDFLAGS = ${CRYPTO_LDFLAGS}
+libkea_cryptolink_la_LIBADD = ${CRYPTO_LIBS} ${CRYPTO_RPATH}
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink.h>
+#include <cryptolink/crypto_hash.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <botan/version.h>
+#include <botan/botan.h>
+#include <botan/hash.h>
+#include <botan/types.h>
+
+#include <cstring>
+
+namespace {
+/// @brief Decode the HashAlgorithm enum into a name usable by Botan
+///
+/// @param algorithm algorithm to be converted
+/// @return text representation of the algorithm name
+const char*
+getBotanHashAlgorithmName(isc::cryptolink::HashAlgorithm algorithm) {
+ switch (algorithm) {
+ case isc::cryptolink::MD5:
+ return ("MD5");
+ case isc::cryptolink::SHA1:
+ return ("SHA-1");
+ case isc::cryptolink::SHA256:
+ return ("SHA-256");
+ case isc::cryptolink::SHA224:
+ return ("SHA-224");
+ case isc::cryptolink::SHA384:
+ return ("SHA-384");
+ case isc::cryptolink::SHA512:
+ return ("SHA-512");
+ case isc::cryptolink::UNKNOWN_HASH:
+ return ("Unknown");
+ }
+ // 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 {
+
+/// @brief Botan implementation of Hash. Each method is the counterpart
+/// of the Hash corresponding method.
+class HashImpl {
+public:
+
+ /// @brief Constructor for specific hash algorithm
+ ///
+ /// @param hash_algorithm The hash algorithm
+ explicit HashImpl(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: " <<
+ static_cast<int>(hash_algorithm));
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+
+ hash_.reset(hash);
+ }
+
+ /// @brief Destructor
+ ~HashImpl() { }
+
+ /// @brief Returns the output size of the digest
+ ///
+ /// @return output size of the digest
+ size_t getOutputLength() const {
+#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
+ return (hash_->output_length());
+#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
+ return (hash_->OUTPUT_LENGTH);
+#else
+#error "Unsupported Botan version (need 1.8 or higher)"
+ // added to suppress irrelevant compiler errors
+ return 0;
+#endif
+ }
+
+ /// @brief Adds data to the digest
+ ///
+ /// See @ref isc::cryptolink::Hash::update() for details.
+ void update(const void* data, const size_t len) {
+ try {
+ hash_->update(static_cast<const Botan::byte*>(data), len);
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+ }
+
+ /// @brief Calculate the final digest
+ ///
+ /// See @ref isc::cryptolink::Hash::final() for details.
+ void final(isc::util::OutputBuffer& result, size_t len) {
+ try {
+ Botan::SecureVector<Botan::byte> b_result(hash_->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());
+ }
+ }
+
+ /// @brief Calculate the final digest
+ ///
+ /// See @ref isc::cryptolink::Hash::final() for details.
+ void final(void* result, size_t len) {
+ try {
+ Botan::SecureVector<Botan::byte> b_result(hash_->final());
+ size_t output_size = getOutputLength();
+ if (output_size > len) {
+ output_size = len;
+ }
+ std::memcpy(result, b_result.begin(), output_size);
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+ }
+
+ /// @brief Calculate the final digest
+ ///
+ /// See @ref isc::cryptolink::Hash::final() for details.
+ std::vector<uint8_t> final(size_t len) {
+ try {
+ Botan::SecureVector<Botan::byte> b_result(hash_->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());
+ }
+ }
+
+private:
+ /// \brief The protected pointer to the Botan HashFunction object
+ boost::scoped_ptr<Botan::HashFunction> hash_;
+};
+
+Hash::Hash(const HashAlgorithm hash_algorithm)
+{
+ impl_ = new HashImpl(hash_algorithm);
+}
+
+Hash::~Hash() {
+ delete impl_;
+}
+
+size_t
+Hash::getOutputLength() const {
+ return (impl_->getOutputLength());
+}
+
+void
+Hash::update(const void* data, const size_t len) {
+ impl_->update(data, len);
+}
+
+void
+Hash::final(isc::util::OutputBuffer& result, size_t len) {
+ impl_->final(result, len);
+}
+
+void
+Hash::final(void* result, size_t len) {
+ impl_->final(result, len);
+}
+
+std::vector<uint8_t>
+Hash::final(size_t len) {
+ return impl_->final(len);
+}
+
+} // namespace cryptolink
+} // namespace isc
--- /dev/null
+// 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/version.h>
+#include <botan/botan.h>
+#include <botan/hmac.h>
+#include <botan/hash.h>
+#include <botan/types.h>
+
+#include <cstring>
+
+namespace {
+
+/// @brief Decode the HashAlgorithm enum into a name usable by Botan
+///
+/// @param algorithm algorithm to be converted
+/// @return text representation of the algorithm name
+const char*
+getBotanHashAlgorithmName(isc::cryptolink::HashAlgorithm algorithm) {
+ switch (algorithm) {
+ case isc::cryptolink::MD5:
+ return ("MD5");
+ case isc::cryptolink::SHA1:
+ return ("SHA-1");
+ case isc::cryptolink::SHA256:
+ return ("SHA-256");
+ case isc::cryptolink::SHA224:
+ return ("SHA-224");
+ case isc::cryptolink::SHA384:
+ return ("SHA-384");
+ case isc::cryptolink::SHA512:
+ return ("SHA-512");
+ case isc::cryptolink::UNKNOWN_HASH:
+ return ("Unknown");
+ }
+ // 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 {
+
+/// @brief Botan implementation of HMAC. Each method is the counterpart
+/// of the HMAC corresponding method.
+class HMACImpl {
+public:
+ /// @brief Constructor from a secret and a hash algorithm
+ ///
+ /// See constructor of the @ref isc::cryptolink::HMAC class for details.
+ ///
+ /// @param secret The secret to sign with
+ /// @param secret_len The length of the secret
+ /// @param hash_algorithm The hash algorithm
+ explicit HMACImpl(const void* secret, size_t secret_len,
+ const HashAlgorithm hash_algorithm) {
+ 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: " <<
+ static_cast<int>(hash_algorithm));
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+
+ hmac_.reset(new Botan::HMAC(hash));
+
+ // If the key length is larger than the block size, we hash the
+ // key itself first.
+ try {
+ // use a temp var so we don't have blocks spanning
+ // preprocessor directives
+#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
+ size_t block_length = hash->hash_block_size();
+#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
+ size_t block_length = hash->HASH_BLOCK_SIZE;
+#else
+#error "Unsupported Botan version (need 1.8 or higher)"
+ // added to suppress irrelevant compiler errors
+ size_t block_length = 0;
+#endif
+ if (secret_len > block_length) {
+ 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 {
+ // Botan 1.8 considers len 0 a bad key. 1.9 does not,
+ // but we won't accept it anyway, and fail early
+ if (secret_len == 0) {
+ isc_throw(BadKey, "Bad HMAC secret length: 0");
+ }
+ 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());
+ }
+ }
+
+ /// @brief Destructor
+ ~HMACImpl() {
+ }
+
+ /// @brief Returns the output size of the digest
+ ///
+ /// @return output size of the digest
+ size_t getOutputLength() const {
+#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
+ return (hmac_->output_length());
+#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
+ return (hmac_->OUTPUT_LENGTH);
+#else
+#error "Unsupported Botan version (need 1.8 or higher)"
+ // added to suppress irrelevant compiler errors
+ return 0;
+#endif
+ }
+
+ /// @brief Add data to digest
+ ///
+ /// See @ref isc::cryptolink::HMAC::update() for details.
+ 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());
+ }
+ }
+
+ /// @brief Calculate the final signature
+ ///
+ /// See @ref isc::cryptolink::HMAC::sign() for details.
+ 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());
+ }
+ }
+
+ /// @brief Calculate the final signature
+ ///
+ /// See @ref isc::cryptolink::HMAC::sign() for details.
+ 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;
+ }
+ std::memcpy(result, b_result.begin(), output_size);
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+ }
+
+ /// @brief Calculate the final signature
+ ///
+ /// See @ref isc::cryptolink::HMAC::sign() for details.
+ 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());
+ }
+ }
+
+
+ /// @brief Verify an existing signature
+ ///
+ /// See @ref isc::cryptolink::HMAC::verify() for details.
+ bool verify(const void* sig, size_t len) {
+ /// @todo 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
+ /// SEE BELOW FOR TEMPORARY CHANGE
+ try {
+ Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
+ if (len < getOutputLength()) {
+ // Currently we don't support truncated signature in TSIG (see
+ // #920). To avoid validating too short signature accidently,
+ // we enforce the standard signature size for the moment.
+ // Once we support truncation correctly, this if-clause should
+ // (and the capitalized comment above) be removed.
+ return (false);
+ }
+ 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:
+ /// \brief The protected pointer to the Botan HMAC object
+ 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));
+}
+
+} // namespace cryptolink
+} // namespace isc
--- /dev/null
+// 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_hash.h>
+#include <cryptolink/crypto_hmac.h>
+
+#include <botan/botan.h>
+
+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_;
+}
+
+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());
+ }
+ }
+}
+
+} // namespace cryptolink
+} // namespace isc
+
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink.h>
+#include <cryptolink/crypto_hash.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <cstring>
+
+namespace isc {
+namespace cryptolink {
+
+void
+digest(const void* data, const size_t data_len,
+ const HashAlgorithm hash_algorithm,
+ isc::util::OutputBuffer& result, size_t len)
+{
+ boost::scoped_ptr<Hash> hash(
+ CryptoLink::getCryptoLink().createHash(hash_algorithm));
+ hash->update(data, data_len);
+ hash->final(result, len);
+}
+
+void
+deleteHash(Hash* hash) {
+ delete hash;
+}
+
+} // namespace cryptolink
+} // namespace isc
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/buffer.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <cryptolink/cryptolink.h>
+
+#ifndef ISC_CRYPTO_HASH_H
+#define ISC_CRYPTO_HASH_H
+
+namespace isc {
+namespace cryptolink {
+
+/// Forward declaration, pimpl style
+class HashImpl;
+
+/// \brief Hash support
+///
+/// This class is used to create Hash digests. Instances
+/// can be created with CryptoLink::createHash()
+///
+class Hash : private boost::noncopyable {
+private:
+ /// \brief Constructor from a hash algorithm
+ ///
+ /// \exception UnsupportedAlgorithmException if the given algorithm
+ /// is unknown or not supported by the underlying library
+ /// \exception LibraryError if there was any unexpected exception
+ /// in the underlying library
+ ///
+ /// \param hash_algorithm The hash algorithm
+ Hash(const HashAlgorithm hash_algorithm);
+
+ friend Hash* CryptoLink::createHash(const HashAlgorithm);
+
+public:
+ /// \brief Destructor
+ ~Hash();
+
+ /// \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 digest
+ ///
+ /// 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 final(isc::util::OutputBuffer& result, size_t len = 0);
+
+ /// \brief Calculate the final digest
+ ///
+ /// 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.
+ ///
+ /// \param result The memory location the digest will be written to
+ /// \param len Specifies the size of the result location available
+ void final(void* result, size_t len);
+
+ /// \brief Calculate the final digest
+ ///
+ /// 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> final(size_t len = 0);
+
+private:
+ HashImpl* impl_;
+};
+
+/// \brief Create an Hash digest for the given data
+///
+/// This is a convenience function that calculates the hash digest,
+/// given a fixed amount of data. Internally it does the same as
+/// creating an Hash object, feeding it the data, and calculating the
+/// resulting digest.
+///
+/// \exception UnsupportedAlgorithm if the given algorithm is unknown
+/// or not supported by the underlying library
+/// \exception LibraryError if there was any unexpected exception
+/// in the underlying library
+///
+/// \param data The data to digest
+/// \param data_len The length of the data
+/// \param hash_algorithm The hash algorithm
+/// \param result The digest 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 digest(const void* data,
+ const size_t data_len,
+ const HashAlgorithm hash_algorithm,
+ isc::util::OutputBuffer& result,
+ size_t len = 0);
+
+/// \brief Delete an Hash object
+void deleteHash(Hash* hash);
+
+} // namespace cryptolink
+} // namespace isc
+
+#endif // ISC_CRYPTO_HASH_H
+
#include <boost/scoped_ptr.hpp>
-#include <botan/version.h>
-#include <botan/botan.h>
-#include <botan/hmac.h>
-#include <botan/hash.h>
-#include <botan/types.h>
-
#include <cstring>
-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::SHA224:
- return ("SHA-224");
- break;
- case isc::cryptolink::SHA384:
- return ("SHA-384");
- break;
- case isc::cryptolink::SHA512:
- return ("SHA-512");
- 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: " <<
- static_cast<int>(hash_algorithm));
- } catch (const Botan::Exception& exc) {
- isc_throw(isc::cryptolink::LibraryError, exc.what());
- }
-
- hmac_.reset(new Botan::HMAC(hash));
-
- // If the key length is larger than the block size, we hash the
- // key itself first.
- try {
- // use a temp var so we don't have blocks spanning
- // preprocessor directives
-#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
- size_t block_length = hash->hash_block_size();
-#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
- size_t block_length = hash->HASH_BLOCK_SIZE;
-#else
-#error "Unsupported Botan version (need 1.8 or higher)"
- // added to suppress irrelevant compiler errors
- size_t block_length = 0;
-#endif
- if (secret_len > block_length) {
- 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 {
- // Botan 1.8 considers len 0 a bad key. 1.9 does not,
- // but we won't accept it anyway, and fail early
- if (secret_len == 0) {
- isc_throw(BadKey, "Bad HMAC secret length: 0");
- }
- 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 {
-#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
- return (hmac_->output_length());
-#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
- return (hmac_->OUTPUT_LENGTH);
-#else
-#error "Unsupported Botan version (need 1.8 or higher)"
- // added to suppress irrelevant compiler errors
- return 0;
-#endif
- }
-
- 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;
- }
- std::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
- // SEE BELOW FOR TEMPORARY CHANGE
- try {
- Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
- if (len < getOutputLength()) {
- // Currently we don't support truncated signature in TSIG (see
- // #920). To avoid validating too short signature accidently,
- // we enforce the standard signature size for the moment.
- // Once we support truncation correctly, this if-clause should
- // (and the capitalized comment above) be removed.
- return (false);
- }
- 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, const size_t data_len, const void* secret,
size_t secret_len, const HashAlgorithm hash_algorithm,
// PERFORMANCE OF THIS SOFTWARE.
#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hash.h>
#include <cryptolink/crypto_hmac.h>
-#include <botan/botan.h>
-
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();
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());
- }
- }
+Hash*
+CryptoLink::createHash(const HashAlgorithm hash_algorithm)
+{
+ return (new Hash(hash_algorithm));
}
HMAC*
};
+// Forward declaration for createHash()
+class Hash;
+
// Forward declaration for createHMAC()
class HMAC;
///
static void initialize();
+ /// \brief Factory function for Hash objects
+ ///
+ /// CryptoLink objects cannot be constructed directly. This
+ /// function creates a new Hash 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.
+ ///
+ /// If you want to safely delete objects created with this method,
+ /// you can use the function deleteHash() as defined in
+ /// crypto_hash.h
+ ///
+ /// \exception UnsupportedAlgorithmException if the given algorithm
+ /// is unknown or not supported by the underlying library
+ /// \exception LibraryError if there was any unexpected exception
+ /// in the underlying library
+ ///
+ /// \param hash_algorithm The hash algorithm
+ Hash* createHash(const HashAlgorithm hash_algorithm);
+
/// \brief Factory function for HMAC objects
///
/// CryptoLink objects cannot be constructed directly. This
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink.h>
+#include <cryptolink/crypto_hash.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <openssl/evp.h>
+
+#include <cstring>
+
+namespace {
+
+/// @brief Decode the HashAlgorithm enum into an EVP_MD pointer (or 0)
+///
+/// EVP_MD pointer is a OpenSSL's way of identifying hash algorithms
+/// @param algorithm algorithm to be converted
+/// @return pointer to EVP_MD which identifies the algorithm
+const EVP_MD*
+getOpenSSLHashAlgorithm(isc::cryptolink::HashAlgorithm algorithm) {
+ switch (algorithm) {
+ case isc::cryptolink::MD5:
+ return (EVP_md5());
+ case isc::cryptolink::SHA1:
+ return (EVP_sha1());
+ case isc::cryptolink::SHA256:
+ return (EVP_sha256());
+ case isc::cryptolink::SHA224:
+ return (EVP_sha224());
+ case isc::cryptolink::SHA384:
+ return (EVP_sha384());
+ case isc::cryptolink::SHA512:
+ return (EVP_sha512());
+ case isc::cryptolink::UNKNOWN_HASH:
+ return (0);
+ }
+ // compiler should have prevented us to reach this, since we have
+ // no default. But we need a return value anyway
+ return (0);
+}
+
+} // local namespace
+
+
+namespace isc {
+namespace cryptolink {
+
+/// \brief OpenSSL implementation of Hash. Each method is the counterpart
+/// of the Hash corresponding method.
+class HashImpl {
+public:
+
+ /// @brief Constructor for specific hash algorithm
+ ///
+ /// @param hash_algorithm The hash algorithm
+ explicit HashImpl(const HashAlgorithm hash_algorithm) {
+ const EVP_MD* algo = getOpenSSLHashAlgorithm(hash_algorithm);
+ if (algo == 0) {
+ isc_throw(isc::cryptolink::UnsupportedAlgorithm,
+ "Unknown hash algorithm: " <<
+ static_cast<int>(hash_algorithm));
+ }
+
+ md_.reset(new EVP_MD_CTX);
+
+ EVP_MD_CTX_init(md_.get());
+
+ EVP_DigestInit_ex(md_.get(), algo, NULL);
+ }
+
+ /// @brief Destrucotr
+ ~HashImpl() {
+ if (md_) {
+ EVP_MD_CTX_cleanup(md_.get());
+ }
+ }
+
+ /// @brief Returns the output size of the digest
+ ///
+ /// @return output size of the digest
+ size_t getOutputLength() const {
+ return (EVP_MD_CTX_size(md_.get()));
+ }
+
+ /// @brief Adds data to the digest
+ ///
+ /// See @ref isc::cryptolink::Hash::update() for details.
+ void update(const void* data, const size_t len) {
+ EVP_DigestUpdate(md_.get(), data, len);
+ }
+
+ /// @brief Calculate the final digest
+ ///
+ /// See @ref isc::cryptolink::Hash::final() for details.
+ void final(isc::util::OutputBuffer& result, size_t len) {
+ size_t size = getOutputLength();
+ std::vector<unsigned char> digest(size);
+ EVP_DigestFinal_ex(md_.get(), &digest[0], NULL);
+ if (len == 0 || len > size) {
+ len = size;
+ }
+ result.writeData(&digest[0], len);
+ }
+
+ /// @brief Calculate the final digest
+ ///
+ /// See @ref isc::cryptolink::Hash::final() for details.
+ void final(void* result, size_t len) {
+ size_t size = getOutputLength();
+ std::vector<unsigned char> digest(size);
+ EVP_DigestFinal_ex(md_.get(), &digest[0], NULL);
+ if (len > size) {
+ len = size;
+ }
+ std::memcpy(result, &digest[0], len);
+ }
+
+ /// @brief Calculate the final digest
+ ///
+ /// See @ref isc::cryptolink::Hash::final() for details.
+ std::vector<uint8_t> final(size_t len) {
+ size_t size = getOutputLength();
+ std::vector<unsigned char> digest(size);
+ EVP_DigestFinal_ex(md_.get(), &digest[0], NULL);
+ if (len != 0 && len < size) {
+ digest.resize(len);
+ }
+ return (std::vector<uint8_t>(digest.begin(), digest.end()));
+ }
+
+private:
+ /// @brief The protected pointer to the OpenSSL EVP_MD_CTX structure
+ boost::scoped_ptr<EVP_MD_CTX> md_;
+};
+
+Hash::Hash(const HashAlgorithm hash_algorithm)
+{
+ impl_ = new HashImpl(hash_algorithm);
+}
+
+Hash::~Hash() {
+ delete impl_;
+}
+
+size_t
+Hash::getOutputLength() const {
+ return (impl_->getOutputLength());
+}
+
+void
+Hash::update(const void* data, const size_t len) {
+ impl_->update(data, len);
+}
+
+void
+Hash::final(isc::util::OutputBuffer& result, size_t len) {
+ impl_->final(result, len);
+}
+
+void
+Hash::final(void* result, size_t len) {
+ impl_->final(result, len);
+}
+
+std::vector<uint8_t>
+Hash::final(size_t len) {
+ return impl_->final(len);
+}
+
+} // namespace cryptolink
+} // namespace isc
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <openssl/hmac.h>
+
+#include <cstring>
+
+namespace {
+
+/// @brief Decode the HashAlgorithm enum into an EVP_MD pointer (or 0)
+///
+/// EVP_MD pointer is a OpenSSL's way of identifying hash algorithms
+/// @param algorithm algorithm to be converted
+/// @return pointer to EVP_MD which identifies the algorithm
+const EVP_MD*
+getOpenSSLHashAlgorithm(isc::cryptolink::HashAlgorithm algorithm) {
+ switch (algorithm) {
+ case isc::cryptolink::MD5:
+ return (EVP_md5());
+ case isc::cryptolink::SHA1:
+ return (EVP_sha1());
+ case isc::cryptolink::SHA256:
+ return (EVP_sha256());
+ case isc::cryptolink::SHA224:
+ return (EVP_sha224());
+ case isc::cryptolink::SHA384:
+ return (EVP_sha384());
+ case isc::cryptolink::SHA512:
+ return (EVP_sha512());
+ case isc::cryptolink::UNKNOWN_HASH:
+ return (0);
+ }
+ // compiler should have prevented us to reach this, since we have
+ // no default. But we need a return value anyway
+ return (0);
+}
+
+/// Secure Buffers which are wiped out when released.
+template<typename T>
+struct SecBuf {
+public:
+ typedef typename std::vector<T>::iterator iterator;
+
+ typedef typename std::vector<T>::const_iterator const_iterator;
+
+ explicit SecBuf() : vec_(std::vector<T>()) {}
+
+ explicit SecBuf(size_t n, const T& value = T()) :
+ vec_(std::vector<T>(n, value))
+ {}
+
+ SecBuf(iterator first, iterator last) :
+ vec_(std::vector<T>(first, last))
+ {}
+
+ SecBuf(const_iterator first, const_iterator last) :
+ vec_(std::vector<T>(first, last))
+ {}
+
+ SecBuf(const std::vector<T>& x) : vec_(x) {}
+
+ ~SecBuf() {
+ std::memset(&vec_[0], 0, vec_.capacity() * sizeof(T));
+ };
+
+ iterator begin() {
+ return (vec_.begin());
+ };
+
+ const_iterator begin() const {
+ return (vec_.begin());
+ };
+
+ iterator end() {
+ return (vec_.end());
+ };
+
+ const_iterator end() const {
+ return (vec_.end());
+ };
+
+ size_t size() const {
+ return (vec_.size());
+ };
+
+ void resize(size_t sz) {
+ vec_.resize(sz);
+ };
+
+ SecBuf& operator=(const SecBuf& x) {
+ if (&x != *this) {
+ vec_ = x.vec_;
+ }
+ return (*this);
+ };
+
+ T& operator[](size_t n) {
+ return (vec_[n]);
+ };
+
+ const T& operator[](size_t n) const {
+ return (vec_[n]);
+ };
+
+private:
+ std::vector<T> vec_;
+};
+
+} // local namespace
+
+namespace isc {
+namespace cryptolink {
+
+/// @brief OpenSSL implementation of HMAC. Each method is the counterpart
+/// of the HMAC corresponding method.
+class HMACImpl {
+public:
+ /// @brief Constructor from a secret and a hash algorithm
+ ///
+ /// See constructor of the @ref isc::cryptolink::HMAC class for details.
+ ///
+ /// @param secret The secret to sign with
+ /// @param secret_len The length of the secret
+ /// @param hash_algorithm The hash algorithm
+ explicit HMACImpl(const void* secret, size_t secret_len,
+ const HashAlgorithm hash_algorithm) {
+ const EVP_MD* algo = getOpenSSLHashAlgorithm(hash_algorithm);
+ if (algo == 0) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown hash algorithm: " <<
+ static_cast<int>(hash_algorithm));
+ }
+ if (secret_len == 0) {
+ isc_throw(BadKey, "Bad HMAC secret length: 0");
+ }
+
+ md_.reset(new HMAC_CTX);
+ HMAC_CTX_init(md_.get());
+
+ HMAC_Init_ex(md_.get(), secret,
+ static_cast<int>(secret_len),
+ algo, NULL);
+ }
+
+ /// @brief Destructor
+ ~HMACImpl() {
+ if (md_) {
+ HMAC_CTX_cleanup(md_.get());
+ }
+ }
+
+ /// @brief Returns the output size of the digest
+ ///
+ /// @return output size of the digest
+ size_t getOutputLength() const {
+ int size = HMAC_size(md_.get());
+ if (size < 0) {
+ isc_throw(isc::cryptolink::LibraryError, "EVP_MD_CTX_size");
+ }
+ return (static_cast<size_t>(size));
+ }
+
+ /// @brief Add data to digest
+ ///
+ /// See @ref isc::cryptolink::HMAC::update() for details.
+ void update(const void* data, const size_t len) {
+ HMAC_Update(md_.get(), static_cast<const unsigned char*>(data), len);
+ }
+
+ /// @brief Calculate the final signature
+ ///
+ /// See @ref isc::cryptolink::HMAC::sign() for details.
+ void sign(isc::util::OutputBuffer& result, size_t len) {
+ size_t size = getOutputLength();
+ SecBuf<unsigned char> digest(size);
+ HMAC_Final(md_.get(), &digest[0], NULL);
+ if (len == 0 || len > size) {
+ len = size;
+ }
+ result.writeData(&digest[0], len);
+ }
+
+ /// @brief Calculate the final signature
+ ///
+ /// See @ref isc::cryptolink::HMAC::sign() for details.
+ void sign(void* result, size_t len) {
+ size_t size = getOutputLength();
+ SecBuf<unsigned char> digest(size);
+ HMAC_Final(md_.get(), &digest[0], NULL);
+ if (len > size) {
+ len = size;
+ }
+ std::memcpy(result, &digest[0], len);
+ }
+
+ /// @brief Calculate the final signature
+ ///
+ /// See @ref isc::cryptolink::HMAC::sign() for details.
+ std::vector<uint8_t> sign(size_t len) {
+ size_t size = getOutputLength();
+ SecBuf<unsigned char> digest(size);
+ HMAC_Final(md_.get(), &digest[0], NULL);
+ if (len != 0 && len < size) {
+ digest.resize(len);
+ }
+ return (std::vector<uint8_t>(digest.begin(), digest.end()));
+ }
+
+ /// @brief Verify an existing signature
+ ///
+ /// See @ref isc::cryptolink::HMAC::verify() for details.
+ bool verify(const void* sig, size_t len) {
+ size_t size = getOutputLength();
+ if (len != 0 && len < size / 2) {
+ return (false);
+ }
+ SecBuf<unsigned char> digest(size);
+ HMAC_Final(md_.get(), &digest[0], NULL);
+ if (len == 0 || len > size) {
+ len = size;
+ }
+ return (std::memcmp(&digest[0], sig, len) == 0);
+ }
+
+private:
+
+ /// @brief The protected pointer to the OpenSSL HMAC_CTX structure
+ boost::scoped_ptr<HMAC_CTX> md_;
+};
+
+HMAC::HMAC(const void* secret, size_t secret_length,
+ const HashAlgorithm hash_algorithm)
+{
+ impl_ = new HMACImpl(secret, secret_length, hash_algorithm);
+}
+
+HMAC::~HMAC() {
+ delete impl_;
+}
+
+size_t
+HMAC::getOutputLength() const {
+ return (impl_->getOutputLength());
+}
+
+void
+HMAC::update(const void* data, const size_t len) {
+ impl_->update(data, len);
+}
+
+void
+HMAC::sign(isc::util::OutputBuffer& result, size_t len) {
+ impl_->sign(result, len);
+}
+
+void
+HMAC::sign(void* result, size_t len) {
+ impl_->sign(result, len);
+}
+
+std::vector<uint8_t>
+HMAC::sign(size_t len) {
+ return impl_->sign(len);
+}
+
+bool
+HMAC::verify(const void* sig, const size_t len) {
+ return (impl_->verify(sig, len));
+}
+
+} // namespace cryptolink
+} // namespace isc
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hash.h>
+#include <cryptolink/crypto_hmac.h>
+
+namespace isc {
+namespace cryptolink {
+
+// For OpenSSL, we use the CryptoLink class object in RAII style
+class CryptoLinkImpl {
+ // empty class
+};
+
+CryptoLink::~CryptoLink() {
+ delete impl_;
+}
+
+void
+CryptoLink::initialize() {
+ CryptoLink& c = getCryptoLinkInternal();
+ if (c.impl_ == NULL) {
+ try {
+ c.impl_ = new CryptoLinkImpl();
+ } catch (const std::exception &ex) {
+ // Should never happen
+ isc_throw(InitializationError,
+ "Error during OpenSSL initialization:" << ex.what());
+ } catch (...) {
+ // Should never happen
+ isc_throw(InitializationError,
+ "Error during OpenSSL initialization");
+ }
+ }
+}
+
+} // namespace cryptolink
+} // namespace isc
+
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += crypto_unittests.cc
+run_unittests_SOURCES += hash_unittests.cc
+run_unittests_SOURCES += hmac_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD) $(BOTAN_LIBS)
+run_unittests_LDFLAGS = $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD) $(CRYPTO_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
#include <config.h>
-#include <string>
-#include <vector>
-
-#include <boost/lexical_cast.hpp>
-
#include <gtest/gtest.h>
#include <util/encode/hex.h>
#include <cryptolink/cryptolink.h>
-#include <cryptolink/crypto_hmac.h>
-
-#include <util/buffer.h>
-#include <exceptions/exceptions.h>
-#include <boost/shared_ptr.hpp>
-
-using boost::lexical_cast;
-using namespace isc::util;
-using namespace isc::util::encode;
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 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);
-}
-
-// Temporarily disabled
-TEST(CryptoLinkTest, DISABLED_HMAC_MD5_RFC2202_SIGN_TRUNCATED) {
- 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);
-}
-
-//
-// 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 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);
-}
-
-// Temporarily disabled
-TEST(CryptoLinkTest, DISABLED_HMAC_SHA1_RFC2202_SIGN_TRUNCATED) {
- 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);
-}
-
-//
-// Test values taken from RFC 4231
-//
-// Test data from RFC4231, including secret key
-// and source data, they are common for sha224/256/384/512
-// so put them together within the separate function.
-void
-doRFC4231Tests(HashAlgorithm hash_algorithm,
- const std::vector<std::vector<uint8_t> >& hmac_list)
-{
- std::vector<std::string> data_list;
- std::vector<std::string> secret_list;
-
- data_list.push_back("Hi There");
- data_list.push_back("what do ya want for nothing?");
- data_list.push_back(std::string(50, 0xdd));
- data_list.push_back(std::string(50, 0xcd));
- data_list.push_back("Test With Truncation");
- data_list.push_back("Test Using Larger Than Block-Size Key - "
- "Hash Key First");
- data_list.push_back("This is a test using a larger than block-size "
- "key and a larger than block-size data. The key "
- "needs to be hashed before being used by the HMAC "
- "algorithm.");
-
- secret_list.push_back(std::string(20, 0x0b));
- secret_list.push_back("Jefe");
- secret_list.push_back(std::string(20, 0xaa));
- const uint8_t secret_array[] = {
- 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
- };
- secret_list.push_back(std::string(secret_array,
- secret_array + sizeof(secret_array)));
- secret_list.push_back(std::string(20, 0x0c));
- secret_list.push_back(std::string(131, 0xaa));
- secret_list.push_back(std::string(131, 0xaa));
-
- // Make sure we provide a consistent size of test data
- ASSERT_EQ(secret_list.size(), data_list.size());
- ASSERT_EQ(secret_list.size(), hmac_list.size());
-
- for (std::vector<std::string>::size_type i = 0;
- i < data_list.size(); ++i) {
- SCOPED_TRACE("RFC4231 HMAC test for algorithm ID: " +
- lexical_cast<std::string>(hash_algorithm) +
- ", data ID: " + lexical_cast<std::string>(i));
- // Until #920 is resolved we have to skip truncation cases.
- if (data_list[i] == "Test With Truncation") {
- continue;
- }
- doHMACTest(data_list[i], secret_list[i].c_str(), secret_list[i].size(),
- hash_algorithm, &hmac_list[i][0], hmac_list[i].size());
- }
-}
-
-TEST(CryptoLinkTest, HMAC_SHA256_RFC4231_SIGN) {
- std::vector<std::vector<uint8_t> > hmac_expected_list(7);
-
- int i = 0;
- decodeHex(
- "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
- hmac_expected_list[i++]);
- decodeHex(
- "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
- hmac_expected_list[i++]);
- decodeHex(
- "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
- hmac_expected_list[i++]);
- decodeHex(
- "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
- hmac_expected_list[i++]);
- decodeHex("a3b6167473100ee06e0c796c2955552b", hmac_expected_list[i++]);
- decodeHex(
- "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
- hmac_expected_list[i++]);
- decodeHex(
- "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
- hmac_expected_list[i++]);
-
- doRFC4231Tests(SHA256, hmac_expected_list);
-}
-
-//
-// Test values taken from RFC 4231, test optional algorithm 224,384,512
-//
-TEST(CryptoLinkTest, HMAC_SHA224_RFC4231_SIGN) {
- std::vector<std::vector<uint8_t> > hmac_expected_list(7);
-
- int i = 0;
- decodeHex("896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22",
- hmac_expected_list[i++]);
- decodeHex("a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44",
- hmac_expected_list[i++]);
- decodeHex("7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea",
- hmac_expected_list[i++]);
- decodeHex("6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a",
- hmac_expected_list[i++]);
- decodeHex("0e2aea68a90c8d37c988bcdb9fca6fa8", hmac_expected_list[i++]);
- decodeHex("95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e",
- hmac_expected_list[i++]);
- decodeHex("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1",
- hmac_expected_list[i++]);
-
- doRFC4231Tests(SHA224, hmac_expected_list);
-}
-
-TEST(CryptoLinkTest, HMAC_SHA384_RFC4231_SIGN) {
- std::vector<std::vector<uint8_t> > hmac_expected_list(7);
-
- int i = 0;
- decodeHex("afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc5"
- "9cfaea9ea9076ede7f4af152e8b2fa9cb6", hmac_expected_list[i++]);
- decodeHex("af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec373632244"
- "5e8e2240ca5e69e2c78b3239ecfab21649", hmac_expected_list[i++]);
- decodeHex("88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e5596614"
- "4b2a5ab39dc13814b94e3ab6e101a34f27", hmac_expected_list[i++]);
- decodeHex("3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b"
- "4e6801dd23c4a7d679ccf8a386c674cffb", hmac_expected_list[i++]);
- decodeHex("3abf34c3503b2a23a46efc619baef897", hmac_expected_list[i++]);
- decodeHex("4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4"
- "c60c2ef6ab4030fe8296248df163f44952", hmac_expected_list[i++]);
- decodeHex("6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99"
- "c5a678cc31e799176d3860e6110c46523e", hmac_expected_list[i++]);
-
- doRFC4231Tests(SHA384, hmac_expected_list);
-}
-
-TEST(CryptoLinkTest, HMAC_SHA512_RFC4231_SIGN) {
- std::vector<std::vector<uint8_t> > hmac_expected_list(7);
-
- int i = 0;
- decodeHex("87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17c"
- "dedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a12"
- "6854", hmac_expected_list[i++]);
- decodeHex("164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505"
- "549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bc"
- "e737", hmac_expected_list[i++]);
- decodeHex("fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d"
- "39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e132"
- "92fb", hmac_expected_list[i++]);
- decodeHex("b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3"
- "dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a2"
- "98dd", hmac_expected_list[i++]);
- decodeHex("415fad6271580a531d4179bc891d87a6", hmac_expected_list[i++]);
- decodeHex("80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3"
- "526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d78"
- "6598", hmac_expected_list[i++]);
- decodeHex("e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc9"
- "44b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c"
- "6a58", hmac_expected_list[i++]);
-
- doRFC4231Tests(SHA512, hmac_expected_list);
-}
-
-TEST(CryptoLinkTest, DISABLED_HMAC_SHA256_RFC2202_SIGN_TRUNCATED) {
- 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);
-}
-
-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);
-}
-
+// Tests whether getCryptoLink() returns a singleton instance
TEST(CryptoLinkTest, Singleton) {
const CryptoLink& c1 = CryptoLink::getCryptoLink();
const CryptoLink& c2 = CryptoLink::getCryptoLink();
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <string>
+#include <vector>
+
+#include <boost/lexical_cast.hpp>
+
+#include <gtest/gtest.h>
+
+#include <util/encode/hex.h>
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hash.h>
+
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/scoped_array.hpp>
+#include <boost/shared_ptr.hpp>
+
+using boost::lexical_cast;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::cryptolink;
+
+namespace {
+ /// @brief Compare data with expected value
+ /// @param data Value to compare
+ /// @param expected Expected value
+ /// @param len Length of the expected value
+ 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]);
+ }
+ }
+
+ /// @brief Compare OutputBuffer with expected value
+ /// encapsulated checkData()
+ /// @param buf buffer to compare
+ /// @param expected Expected value
+ /// @param len Length of the expected value
+ 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);
+ }
+
+ /// @brief Hash with the convenience functions
+ /// See @ref doHashTest for parameters
+ void doHashTestConv(const std::string& data,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hash,
+ size_t hash_len) {
+ OutputBuffer data_buf(data.size());
+ data_buf.writeData(data.c_str(), data.size());
+ OutputBuffer hash_digest(0);
+
+ // Sign it
+ digest(data_buf.getData(), data_buf.getLength(),
+ hash_algorithm, hash_digest, hash_len);
+
+ // Check if the signature is what we expect
+ checkBuffer(hash_digest, expected_hash, hash_len);
+ }
+
+ /// @brief Hash with an instantiation of a Hash object
+ /// See @ref doHashTest for parameters
+ void doHashTestDirect(const std::string& data,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hash,
+ size_t hash_len) {
+ OutputBuffer data_buf(data.size());
+ data_buf.writeData(data.c_str(), data.size());
+ OutputBuffer result(1);
+ CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+ // Do it
+ boost::shared_ptr<Hash> hash_digest(crypto.createHash(hash_algorithm),
+ deleteHash);
+ hash_digest->update(data_buf.getData(), data_buf.getLength());
+ hash_digest->final(result, hash_len);
+
+ // Check if the digest is what we expect
+ checkBuffer(result, expected_hash, hash_len);
+ }
+
+ /// @brief Hash with a vector representation
+ /// See @ref doHashTest for parameters
+ void doHashTestVector(const std::string& data,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hash,
+ size_t hash_len) {
+ CryptoLink& crypto = CryptoLink::getCryptoLink();
+ boost::shared_ptr<Hash> hash_digest(crypto.createHash(hash_algorithm),
+ deleteHash);
+ hash_digest->update(data.c_str(), data.size());
+ std::vector<uint8_t> result = hash_digest->final(hash_len);
+ ASSERT_EQ(hash_len, result.size());
+ checkData(&result[0], expected_hash, hash_len);
+ }
+
+ /// @brief Hash with an array representation
+ /// See @ref doHashTest for parameters
+ void doHashTestArray(const std::string& data,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hash,
+ size_t hash_len) {
+ CryptoLink& crypto = CryptoLink::getCryptoLink();
+ boost::shared_ptr<Hash> hash_digest(crypto.createHash(hash_algorithm),
+ deleteHash);
+ hash_digest->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.
+ boost::scoped_array<uint8_t> result(new uint8_t[hash_len]);
+
+ hash_digest->final(result.get(), hash_len);
+ checkData(result.get(), expected_hash, hash_len);
+ }
+
+ /// @brief Hash using all variants
+ /// @param data Input value
+ /// @param hash_algorithm Hash algorithm enum
+ /// @param expected_hash Expected value
+ /// @param hash_len Expected value length
+ void doHashTest(const std::string& data,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hash,
+ size_t hash_len) {
+ doHashTestConv(data, hash_algorithm, expected_hash, hash_len);
+ doHashTestDirect(data, hash_algorithm, expected_hash, hash_len);
+ doHashTestVector(data, hash_algorithm, expected_hash, hash_len);
+ doHashTestArray(data, hash_algorithm, expected_hash, hash_len);
+ }
+}
+
+//
+// Test values taken from RFC 1321
+//
+TEST(HashTest, MD5_RFC1321) {
+ const uint8_t hash_expected[] = { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f,
+ 0x00, 0xb2, 0x04, 0xe9, 0x80,
+ 0x09, 0x98, 0xec, 0xf8, 0x42,
+ 0x7e };
+ doHashTest("", MD5, hash_expected, 16);
+
+ const uint8_t hash_expected2[] = { 0x0c, 0xc1, 0x75, 0xb9, 0xc0,
+ 0xf1, 0xb6, 0xa8, 0x31, 0xc3,
+ 0x99, 0xe2, 0x69, 0x77, 0x26,
+ 0x61 };
+ doHashTest("a", MD5, hash_expected2, 16);
+
+ const uint8_t hash_expected3[] = { 0x90, 0x01, 0x50, 0x98, 0x3c,
+ 0xd2, 0x4f, 0xb0, 0xd6, 0x96,
+ 0x3f, 0x7d, 0x28, 0xe1, 0x7f,
+ 0x72 };
+ doHashTest("abc", MD5, hash_expected3, 16);
+
+ const uint8_t hash_expected4[] = { 0xf9, 0x6b, 0x69, 0x7d, 0x7c,
+ 0xb7, 0x93, 0x8d, 0x52, 0x5a,
+ 0x2f, 0x31, 0xaa, 0xf1, 0x61,
+ 0xd0 };
+ doHashTest("message digest", MD5, hash_expected4, 16);
+
+ const uint8_t hash_expected6[] = { 0xc3, 0xfc, 0xd3, 0xd7, 0x61,
+ 0x92, 0xe4, 0x00, 0x7d, 0xfb,
+ 0x49, 0x6c, 0xca, 0x67, 0xe1,
+ 0x3b };
+ doHashTest("abcdefghijklmnopqrstuvwxyz", MD5, hash_expected6, 16);
+
+ const uint8_t hash_expected7[] = { 0xd1, 0x74, 0xab, 0x98, 0xd2,
+ 0x77, 0xd9, 0xf5, 0xa5, 0x61,
+ 0x1c, 0x2c, 0x9f, 0x41, 0x9d,
+ 0x9f };
+ doHashTest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcd"
+ "efghijklmnopqrstuvwxyz0123456789",
+ MD5, hash_expected7, 16);
+
+ const uint8_t hash_expected8[] = { 0x57, 0xed, 0xf4, 0xa2, 0x2b,
+ 0xe3, 0xc9, 0x55, 0xac, 0x49,
+ 0xda, 0x2e, 0x21, 0x07, 0xb6,
+ 0x7a };
+ doHashTest("1234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890",
+ MD5, hash_expected8, 16);
+}
+
+//
+// Test values taken from RFC 3174
+//
+TEST(HashTest, SHA1_RFC3174) {
+ const uint8_t hash_expected[] = { 0xa9, 0x99, 0x3e, 0x36, 0x47,
+ 0x06, 0x81, 0x6a, 0xba, 0x3e,
+ 0x25, 0x71, 0x78, 0x50, 0xc2,
+ 0x6c, 0x9c, 0xd0, 0xd8, 0x9d };
+ doHashTest("abc", SHA1, hash_expected, 20);
+
+ const uint8_t hash_expected2[] = { 0x84, 0x98, 0x3e, 0x44, 0x1c,
+ 0x3b, 0xd2, 0x6e, 0xba, 0xae,
+ 0x4a, 0xa1, 0xf9, 0x51, 0x29,
+ 0xe5, 0xe5, 0x46, 0x70, 0xf1 };
+ doHashTest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ SHA1, hash_expected2, 20);
+
+ const uint8_t hash_expected3[] = { 0x34, 0xaa, 0x97, 0x3c, 0xd4,
+ 0xc4, 0xda, 0xa4, 0xf6, 0x1e,
+ 0xeb, 0x2b, 0xdb, 0xad, 0x27,
+ 0x31, 0x65, 0x34, 0x01, 0x6f };
+ doHashTest(std::string(1000000, 0x61), SHA1, hash_expected3, 20);
+
+ const uint8_t hash_expected4[] = { 0xde, 0xa3, 0x56, 0xa2, 0xcd,
+ 0xdd, 0x90, 0xc7, 0xa7, 0xec,
+ 0xed, 0xc5, 0xeb, 0xb5, 0x63,
+ 0x93, 0x4f, 0x46, 0x04, 0x52 };
+ doHashTest("01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567",
+ SHA1, hash_expected4, 20);
+}
+
+//
+// Test values taken from RFC 6234
+//
+TEST(HashTest, SHA224_RFC6234) {
+ const uint8_t hash_expected[] = { 0x23, 0x09, 0x7d, 0x22, 0x34,
+ 0x05, 0xd8, 0x22, 0x86, 0x42,
+ 0xa4, 0x77, 0xbd, 0xa2, 0x55,
+ 0xb3, 0x2a, 0xad, 0xbc, 0xe4,
+ 0xbd, 0xa0, 0xb3, 0xf7, 0xe3,
+ 0x6c, 0x9d, 0xa7 };
+ doHashTest("abc", SHA224, hash_expected, 28);
+
+ const uint8_t hash_expected2[] = { 0x75, 0x38, 0x8b, 0x16, 0x51,
+ 0x27, 0x76, 0xcc, 0x5d, 0xba,
+ 0x5d, 0xa1, 0xfd, 0x89, 0x01,
+ 0x50, 0xb0, 0xc6, 0x45, 0x5c,
+ 0xb4, 0xf5, 0x8b, 0x19, 0x52,
+ 0x52, 0x25, 0x25 };
+ doHashTest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ SHA224, hash_expected2, 28);
+
+ const uint8_t hash_expected3[] = { 0x20, 0x79, 0x46, 0x55, 0x98,
+ 0x0c, 0x91, 0xd8, 0xbb, 0xb4,
+ 0xc1, 0xea, 0x97, 0x61, 0x8a,
+ 0x4b, 0xf0, 0x3f, 0x42, 0x58,
+ 0x19, 0x48, 0xb2, 0xee, 0x4e,
+ 0xe7, 0xad, 0x67 };
+ doHashTest(std::string(1000000, 0x61), SHA224, hash_expected3, 28);
+
+ const uint8_t hash_expected4[] = { 0x56, 0x7f, 0x69, 0xf1, 0x68,
+ 0xcd, 0x78, 0x44, 0xe6, 0x52,
+ 0x59, 0xce, 0x65, 0x8f, 0xe7,
+ 0xaa, 0xdf, 0xa2, 0x52, 0x16,
+ 0xe6, 0x8e, 0xca, 0x0e, 0xb7,
+ 0xab, 0x82, 0x62 };
+ doHashTest("01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567",
+ SHA224, hash_expected4, 28);
+}
+
+TEST(HashTest, SHA256_RFC6234) {
+ const uint8_t hash_expected[] = { 0xba, 0x78, 0x16, 0xbf, 0x8f,
+ 0x01, 0xcf, 0xea, 0x41, 0x41,
+ 0x40, 0xde, 0x5d, 0xae, 0x22,
+ 0x23, 0xb0, 0x03, 0x61, 0xa3,
+ 0x96, 0x17, 0x7a, 0x9c, 0xb4,
+ 0x10, 0xff, 0x61, 0xf2, 0x00,
+ 0x15, 0xad };
+ doHashTest("abc", SHA256, hash_expected, 32);
+
+ const uint8_t hash_expected2[] = { 0x24, 0x8d, 0x6a, 0x61, 0xd2,
+ 0x06, 0x38, 0xb8, 0xe5, 0xc0,
+ 0x26, 0x93, 0x0c, 0x3e, 0x60,
+ 0x39, 0xa3, 0x3c, 0xe4, 0x59,
+ 0x64, 0xff, 0x21, 0x67, 0xf6,
+ 0xec, 0xed, 0xd4, 0x19, 0xdb,
+ 0x06, 0xc1 };
+ doHashTest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ SHA256, hash_expected2, 32);
+
+ const uint8_t hash_expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c, 0x99,
+ 0x14, 0xfb, 0x92, 0x81, 0xa1,
+ 0xc7, 0xe2, 0x84, 0xd7, 0x3e,
+ 0x67, 0xf1, 0x80, 0x9a, 0x48,
+ 0xa4, 0x97, 0x20, 0x0e, 0x04,
+ 0x6d, 0x39, 0xcc, 0xc7, 0x11,
+ 0x2c, 0xd0 };
+ doHashTest(std::string(1000000, 0x61), SHA256, hash_expected3, 32);
+
+ const uint8_t hash_expected4[] = { 0x59, 0x48, 0x47, 0x32, 0x84,
+ 0x51, 0xbd, 0xfa, 0x85, 0x05,
+ 0x62, 0x25, 0x46, 0x2c, 0xc1,
+ 0xd8, 0x67, 0xd8, 0x77, 0xfb,
+ 0x38, 0x8d, 0xf0, 0xce, 0x35,
+ 0xf2, 0x5a, 0xb5, 0x56, 0x2b,
+ 0xfb, 0xb5 };
+ doHashTest("01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567",
+ SHA256, hash_expected4, 32);
+}
+
+TEST(HashTest, SHA384_RFC6234) {
+ const uint8_t hash_expected[] = { 0xcb, 0x00, 0x75, 0x3f, 0x45,
+ 0xa3, 0x5e, 0x8b, 0xb5, 0xa0,
+ 0x3d, 0x69, 0x9a, 0xc6, 0x50,
+ 0x07, 0x27, 0x2c, 0x32, 0xab,
+ 0x0e, 0xde, 0xd1, 0x63, 0x1a,
+ 0x8b, 0x60, 0x5a, 0x43, 0xff,
+ 0x5b, 0xed, 0x80, 0x86, 0x07,
+ 0x2b, 0xa1, 0xe7, 0xcc, 0x23,
+ 0x58, 0xba, 0xec, 0xa1, 0x34,
+ 0xc8, 0x25, 0xa7 };
+ doHashTest("abc", SHA384, hash_expected, 48);
+
+ const uint8_t hash_expected2[] = { 0x09, 0x33, 0x0c, 0x33, 0xf7,
+ 0x11, 0x47, 0xe8, 0x3d, 0x19,
+ 0x2f, 0xc7, 0x82, 0xcd, 0x1b,
+ 0x47, 0x53, 0x11, 0x1b, 0x17,
+ 0x3b, 0x3b, 0x05, 0xd2, 0x2f,
+ 0xa0, 0x80, 0x86, 0xe3, 0xb0,
+ 0xf7, 0x12, 0xfc, 0xc7, 0xc7,
+ 0x1a, 0x55, 0x7e, 0x2d, 0xb9,
+ 0x66, 0xc3, 0xe9, 0xfa, 0x91,
+ 0x74, 0x60, 0x39 };
+ doHashTest("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+ "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ SHA384, hash_expected2, 48);
+
+ const uint8_t hash_expected3[] = { 0x9d, 0x0e, 0x18, 0x09, 0x71,
+ 0x64, 0x74, 0xcb, 0x08, 0x6e,
+ 0x83, 0x4e, 0x31, 0x0a, 0x4a,
+ 0x1c, 0xed, 0x14, 0x9e, 0x9c,
+ 0x00, 0xf2, 0x48, 0x52, 0x79,
+ 0x72, 0xce, 0xc5, 0x70, 0x4c,
+ 0x2a, 0x5b, 0x07, 0xb8, 0xb3,
+ 0xdc, 0x38, 0xec, 0xc4, 0xeb,
+ 0xae, 0x97, 0xdd, 0xd8, 0x7f,
+ 0x3d, 0x89, 0x85 };
+ doHashTest(std::string(1000000, 0x61), SHA384, hash_expected3, 48);
+
+ const uint8_t hash_expected4[] = { 0x2f, 0xc6, 0x4a, 0x4f, 0x50,
+ 0x0d, 0xdb, 0x68, 0x28, 0xf6,
+ 0xa3, 0x43, 0x0b, 0x8d, 0xd7,
+ 0x2a, 0x36, 0x8e, 0xb7, 0xf3,
+ 0xa8, 0x32, 0x2a, 0x70, 0xbc,
+ 0x84, 0x27, 0x5b, 0x9c, 0x0b,
+ 0x3a, 0xb0, 0x0d, 0x27, 0xa5,
+ 0xcc, 0x3c, 0x2d, 0x22, 0x4a,
+ 0xa6, 0xb6, 0x1a, 0x0d, 0x79,
+ 0xfb, 0x45, 0x96 };
+ doHashTest("01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567",
+ SHA384, hash_expected4, 48);
+}
+
+TEST(HashTest, SHA512_RFC6234) {
+ const uint8_t hash_expected[] = { 0xdd, 0xaf, 0x35, 0xa1, 0x93,
+ 0x61, 0x7a, 0xba, 0xcc, 0x41,
+ 0x73, 0x49, 0xae, 0x20, 0x41,
+ 0x31, 0x12, 0xe6, 0xfa, 0x4e,
+ 0x89, 0xa9, 0x7e, 0xa2, 0x0a,
+ 0x9e, 0xee, 0xe6, 0x4b, 0x55,
+ 0xd3, 0x9a, 0x21, 0x92, 0x99,
+ 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+ 0x36, 0xba, 0x3c, 0x23, 0xa3,
+ 0xfe, 0xeb, 0xbd, 0x45, 0x4d,
+ 0x44, 0x23, 0x64, 0x3c, 0xe8,
+ 0x0e, 0x2a, 0x9a, 0xc9, 0x4f,
+ 0xa5, 0x4c, 0xa4, 0x9f };
+ doHashTest("abc", SHA512, hash_expected, 64);
+
+ const uint8_t hash_expected2[] = { 0x8e, 0x95, 0x9b, 0x75, 0xda,
+ 0xe3, 0x13, 0xda, 0x8c, 0xf4,
+ 0xf7, 0x28, 0x14, 0xfc, 0x14,
+ 0x3f, 0x8f, 0x77, 0x79, 0xc6,
+ 0xeb, 0x9f, 0x7f, 0xa1, 0x72,
+ 0x99, 0xae, 0xad, 0xb6, 0x88,
+ 0x90, 0x18, 0x50, 0x1d, 0x28,
+ 0x9e, 0x49, 0x00, 0xf7, 0xe4,
+ 0x33, 0x1b, 0x99, 0xde, 0xc4,
+ 0xb5, 0x43, 0x3a, 0xc7, 0xd3,
+ 0x29, 0xee, 0xb6, 0xdd, 0x26,
+ 0x54, 0x5e, 0x96, 0xe5, 0x5b,
+ 0x87, 0x4b, 0xe9, 0x09 };
+ doHashTest("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+ "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ SHA512, hash_expected2, 64);
+
+ const uint8_t hash_expected3[] = { 0xe7, 0x18, 0x48, 0x3d, 0x0c,
+ 0xe7, 0x69, 0x64, 0x4e, 0x2e,
+ 0x42, 0xc7, 0xbc, 0x15, 0xb4,
+ 0x63, 0x8e, 0x1f, 0x98, 0xb1,
+ 0x3b, 0x20, 0x44, 0x28, 0x56,
+ 0x32, 0xa8, 0x03, 0xaf, 0xa9,
+ 0x73, 0xeb, 0xde, 0x0f, 0xf2,
+ 0x44, 0x87, 0x7e, 0xa6, 0x0a,
+ 0x4c, 0xb0, 0x43, 0x2c, 0xe5,
+ 0x77, 0xc3, 0x1b, 0xeb, 0x00,
+ 0x9c, 0x5c, 0x2c, 0x49, 0xaa,
+ 0x2e, 0x4e, 0xad, 0xb2, 0x17,
+ 0xad, 0x8c, 0xc0, 0x9b };
+ doHashTest(std::string(1000000, 0x61), SHA512, hash_expected3, 64);
+
+ const uint8_t hash_expected4[] = { 0x89, 0xd0, 0x5b, 0xa6, 0x32,
+ 0xc6, 0x99, 0xc3, 0x12, 0x31,
+ 0xde, 0xd4, 0xff, 0xc1, 0x27,
+ 0xd5, 0xa8, 0x94, 0xda, 0xd4,
+ 0x12, 0xc0, 0xe0, 0x24, 0xdb,
+ 0x87, 0x2d, 0x1a, 0xbd, 0x2b,
+ 0xa8, 0x14, 0x1a, 0x0f, 0x85,
+ 0x07, 0x2a, 0x9b, 0xe1, 0xe2,
+ 0xaa, 0x04, 0xcf, 0x33, 0xc7,
+ 0x65, 0xcb, 0x51, 0x08, 0x13,
+ 0xa3, 0x9c, 0xd5, 0xa8, 0x4c,
+ 0x4a, 0xca, 0xa6, 0x4d, 0x3f,
+ 0x3f, 0xb7, 0xba, 0xe9 };
+ doHashTest("01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567"
+ "01234567012345670123456701234567",
+ SHA512, hash_expected4, 64);
+}
+
+namespace {
+ /// @brief Compute the vector digest length
+ /// @param alg Hash algorithm enum
+ /// @param len Wanted length
+ /// @return Effective length
+ size_t
+ digestVectorLength(HashAlgorithm alg, size_t len) {
+ boost::shared_ptr<Hash> hash_digest(
+ CryptoLink::getCryptoLink().createHash(alg),
+ deleteHash);
+ hash_digest->update("asdf", 4);
+ const std::vector<uint8_t> result = hash_digest->final(len);
+ return (result.size());
+ }
+
+ /// @brief Compute the buffer digest length
+ /// @param alg Hash algorithm enum
+ /// @param len Wanted length
+ /// @return Effective length
+ size_t
+ digestBufferLength(HashAlgorithm alg, size_t len) {
+ boost::shared_ptr<Hash> hash_digest(
+ CryptoLink::getCryptoLink().createHash(alg),
+ deleteHash);
+ hash_digest->update("asdf", 4);
+ OutputBuffer result(0);
+ hash_digest->final(result, len);
+ return (result.getLength());
+ }
+ // There is no equivalent for array digest because it is copied
+ // in place
+}
+
+TEST(HashTest, HashLength) {
+ std::vector<uint8_t> result;
+
+ EXPECT_EQ(16, digestVectorLength(MD5, 0));
+ EXPECT_EQ(8, digestVectorLength(MD5, 8));
+ EXPECT_EQ(16, digestVectorLength(MD5, 16));
+ EXPECT_EQ(16, digestVectorLength(MD5, 40));
+ EXPECT_EQ(16, digestVectorLength(MD5, 2000));
+
+ EXPECT_EQ(20, digestBufferLength(SHA1, 0));
+ EXPECT_EQ(8, digestBufferLength(SHA1, 8));
+ EXPECT_EQ(20, digestBufferLength(SHA1, 20));
+ EXPECT_EQ(20, digestBufferLength(SHA1, 40));
+ EXPECT_EQ(20, digestBufferLength(SHA1, 2000));
+
+ EXPECT_EQ(32, digestBufferLength(SHA256, 0));
+ EXPECT_EQ(8, digestBufferLength(SHA256, 8));
+ EXPECT_EQ(32, digestBufferLength(SHA256, 32));
+ EXPECT_EQ(32, digestBufferLength(SHA256, 40));
+ EXPECT_EQ(32, digestBufferLength(SHA256, 3200));
+
+ EXPECT_EQ(16, digestBufferLength(MD5, 0));
+ EXPECT_EQ(8, digestBufferLength(MD5, 8));
+ EXPECT_EQ(16, digestBufferLength(MD5, 16));
+ EXPECT_EQ(16, digestBufferLength(MD5, 40));
+ EXPECT_EQ(16, digestBufferLength(MD5, 2000));
+
+ EXPECT_EQ(20, digestBufferLength(SHA1, 0));
+ EXPECT_EQ(8, digestBufferLength(SHA1, 8));
+ EXPECT_EQ(20, digestBufferLength(SHA1, 20));
+ EXPECT_EQ(20, digestBufferLength(SHA1, 40));
+ EXPECT_EQ(20, digestBufferLength(SHA1, 2000));
+
+ EXPECT_EQ(32, digestBufferLength(SHA256, 0));
+ EXPECT_EQ(8, digestBufferLength(SHA256, 8));
+ EXPECT_EQ(32, digestBufferLength(SHA256, 32));
+ EXPECT_EQ(32, digestBufferLength(SHA256, 40));
+ EXPECT_EQ(32, digestBufferLength(SHA256, 3200));
+}
+
+// @todo Error cases?
--- /dev/null
+// 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 <string>
+#include <vector>
+
+#include <boost/lexical_cast.hpp>
+
+#include <gtest/gtest.h>
+
+#include <util/encode/hex.h>
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+using boost::lexical_cast;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::cryptolink;
+
+namespace {
+ /// @brief Compare data with expected value
+ /// @param data Value to compare
+ /// @param expected Expected value
+ /// @param len Length of the expected value
+ 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]);
+ }
+ }
+
+ /// @brief Compare OutputBuffer with expected value
+ /// encapsulated checkData()
+ /// @param buf buffer to compare
+ /// @param expected Expected value
+ /// @param len Length of the expected value
+ 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);
+ }
+
+ /// @brief Sign and verify with the convenience functions
+ /// See @ref doHMACTest for parameters
+ 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()));
+ }
+
+ /// @brief Sign and verify with an instantiation of an HMAC object
+ /// See @ref doHMACTest for parameters
+ 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()));
+ }
+
+ /// @brief Sign and verify with vector representation of signature
+ /// See @ref doHMACTest for parameters
+ 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()));
+ }
+
+ /// @brief Sign and verify with array representation of signature
+ /// See @ref doHMACTest for parameters
+ 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;
+ }
+
+ /// @brief Sign and verify using all variants
+ /// @param data Input value
+ /// @param secret Secret value
+ /// @param secret_len Secret value length
+ /// @param hash_algorithm Hash algorithm enum
+ /// @param expected_hmac Expected value
+ /// @param hmac_len Expected value length
+ 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(HMACTest, 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);
+#ifndef WITH_BOTAN
+ doHMACTest("Test With Truncation", secret5, 16, MD5,
+ hmac_expected5, 12);
+#endif
+
+ 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);
+}
+
+// Temporarily disabled
+TEST(HMACTest, HMAC_MD5_RFC2202_SIGN_TRUNCATED) {
+ 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);
+#ifndef WITH_BOTAN
+ doHMACTest("Test With Truncation", secret5, 16, MD5,
+ hmac_expected5, 12);
+#endif
+}
+
+//
+// Test values taken from RFC 2202
+//
+TEST(HMACTest, 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);
+#ifndef WITH_BOTAN
+ doHMACTest("Test With Truncation", secret5, 20, SHA1,
+ hmac_expected5, 12);
+#endif
+
+ 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);
+}
+
+// Temporarily disabled
+TEST(HMACTest, HMAC_SHA1_RFC2202_SIGN_TRUNCATED) {
+ 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);
+#ifndef WITH_BOTAN
+ doHMACTest("Test With Truncation", secret5, 20, SHA1,
+ hmac_expected5, 12);
+#endif
+}
+
+//
+// Test values taken from RFC 4231
+//
+// Test data from RFC4231, including secret key
+// and source data, they are common for sha224/256/384/512
+// so put them together within the separate function.
+void
+doRFC4231Tests(HashAlgorithm hash_algorithm,
+ const std::vector<std::vector<uint8_t> >& hmac_list)
+{
+ std::vector<std::string> data_list;
+ std::vector<std::string> secret_list;
+
+ data_list.push_back("Hi There");
+ data_list.push_back("what do ya want for nothing?");
+ data_list.push_back(std::string(50, 0xdd));
+ data_list.push_back(std::string(50, 0xcd));
+ data_list.push_back("Test With Truncation");
+ data_list.push_back("Test Using Larger Than Block-Size Key - "
+ "Hash Key First");
+ data_list.push_back("This is a test using a larger than block-size "
+ "key and a larger than block-size data. The key "
+ "needs to be hashed before being used by the HMAC "
+ "algorithm.");
+
+ secret_list.push_back(std::string(20, 0x0b));
+ secret_list.push_back("Jefe");
+ secret_list.push_back(std::string(20, 0xaa));
+ const uint8_t secret_array[] = {
+ 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
+ };
+ secret_list.push_back(std::string(secret_array,
+ secret_array + sizeof(secret_array)));
+ secret_list.push_back(std::string(20, 0x0c));
+ secret_list.push_back(std::string(131, 0xaa));
+ secret_list.push_back(std::string(131, 0xaa));
+
+ // Make sure we provide a consistent size of test data
+ ASSERT_EQ(secret_list.size(), data_list.size());
+ ASSERT_EQ(secret_list.size(), hmac_list.size());
+
+ for (std::vector<std::string>::size_type i = 0;
+ i < data_list.size(); ++i) {
+ SCOPED_TRACE("RFC4231 HMAC test for algorithm ID: " +
+ lexical_cast<std::string>(hash_algorithm) +
+ ", data ID: " + lexical_cast<std::string>(i));
+ // Until #920 is resolved we have to skip truncation cases.
+ if (data_list[i] == "Test With Truncation") {
+ continue;
+ }
+ doHMACTest(data_list[i], secret_list[i].c_str(), secret_list[i].size(),
+ hash_algorithm, &hmac_list[i][0], hmac_list[i].size());
+ }
+}
+
+TEST(HMACTest, HMAC_SHA256_RFC4231_SIGN) {
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex(
+ "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
+ hmac_expected_list[i++]);
+ decodeHex("a3b6167473100ee06e0c796c2955552b", hmac_expected_list[i++]);
+ decodeHex(
+ "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
+ hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA256, hmac_expected_list);
+}
+
+//
+// Test values taken from RFC 4231, test optional algorithm 224,384,512
+//
+TEST(HMACTest, HMAC_SHA224_RFC4231_SIGN) {
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex("896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22",
+ hmac_expected_list[i++]);
+ decodeHex("a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44",
+ hmac_expected_list[i++]);
+ decodeHex("7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea",
+ hmac_expected_list[i++]);
+ decodeHex("6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a",
+ hmac_expected_list[i++]);
+ decodeHex("0e2aea68a90c8d37c988bcdb9fca6fa8", hmac_expected_list[i++]);
+ decodeHex("95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e",
+ hmac_expected_list[i++]);
+ decodeHex("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1",
+ hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA224, hmac_expected_list);
+}
+
+TEST(HMACTest, HMAC_SHA384_RFC4231_SIGN) {
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex("afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc5"
+ "9cfaea9ea9076ede7f4af152e8b2fa9cb6", hmac_expected_list[i++]);
+ decodeHex("af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec373632244"
+ "5e8e2240ca5e69e2c78b3239ecfab21649", hmac_expected_list[i++]);
+ decodeHex("88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e5596614"
+ "4b2a5ab39dc13814b94e3ab6e101a34f27", hmac_expected_list[i++]);
+ decodeHex("3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b"
+ "4e6801dd23c4a7d679ccf8a386c674cffb", hmac_expected_list[i++]);
+ decodeHex("3abf34c3503b2a23a46efc619baef897", hmac_expected_list[i++]);
+ decodeHex("4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4"
+ "c60c2ef6ab4030fe8296248df163f44952", hmac_expected_list[i++]);
+ decodeHex("6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99"
+ "c5a678cc31e799176d3860e6110c46523e", hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA384, hmac_expected_list);
+}
+
+TEST(HMACTest, HMAC_SHA512_RFC4231_SIGN) {
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex("87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17c"
+ "dedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a12"
+ "6854", hmac_expected_list[i++]);
+ decodeHex("164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505"
+ "549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bc"
+ "e737", hmac_expected_list[i++]);
+ decodeHex("fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d"
+ "39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e132"
+ "92fb", hmac_expected_list[i++]);
+ decodeHex("b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3"
+ "dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a2"
+ "98dd", hmac_expected_list[i++]);
+ decodeHex("415fad6271580a531d4179bc891d87a6", hmac_expected_list[i++]);
+ decodeHex("80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3"
+ "526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d78"
+ "6598", hmac_expected_list[i++]);
+ decodeHex("e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc9"
+ "44b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c"
+ "6a58", hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA512, hmac_expected_list);
+}
+
+#ifndef WITH_BOTAN
+TEST(HMACTest, HMAC_SHA256_RFC2202_SIGN_TRUNCATED) {
+#else
+TEST(HMACTest, DISABLED_HMAC_SHA256_RFC2202_SIGN_TRUNCATED) {
+#endif
+ 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);
+}
+
+namespace {
+ /// @brief Compute the vector signature length
+ /// @param alg Hash algorithm enum
+ /// @param len Wanted length
+ /// @return Effective length
+ 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());
+ }
+
+ /// @brief Compute the buffer signature length
+ /// @param alg Hash algorithm enum
+ /// @param len Wanted length
+ /// @return Effective length
+ 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());
+ }
+
+ // There is no equivalent for array signature because it is copied
+ // in place
+}
+
+TEST(HMACTest, 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));
+}
+
+// Error cases (not only BadKey)
+TEST(HMACTest, 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);
+}
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
# Some versions of GCC warn about some versions of Boost regarding
libkea_dhcp_ddns_la_CXXFLAGS = $(AM_CXXFLAGS)
libkea_dhcp_ddns_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
libkea_dhcp_ddns_la_LDFLAGS = $(AM_LDFLAGS)
-libkea_dhcp_ddns_la_LDFLAGS += ${BOTAN_LDFLAGS}
+libkea_dhcp_ddns_la_LDFLAGS += ${CRYPTO_LDFLAGS}
libkea_dhcp_ddns_la_LIBADD =
libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
-libkea_dhcp_ddns_la_LIBADD += ${BOTAN_LIBS} ${BOTAN_RPATH}
+libkea_dhcp_ddns_la_LIBADD += ${CRYPTO_LIBS} ${CRYPTO_RPATH}
if USE_CLANGPP
# Disable unused parameter warning caused by some of the
#include <asiolink/io_address.h>
#include <asiolink/io_error.h>
#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hash.h>
#include <boost/algorithm/string/predicate.hpp>
-#include <botan/sha2_32.h>
#include <sstream>
#include <limits>
// Use the DUID and FQDN to compute the digest (see RFC4701, section 3).
- // The getCryptoLink is a common function to initialize the Botan library.
- cryptolink::CryptoLink::getCryptoLink();
- // @todo The code below, which calculates the SHA-256 may need to be moved
- // to the cryptolink library.
- Botan::SecureVector<Botan::byte> secure;
+ isc::util::OutputBuffer hash(0);
try {
- Botan::SHA_256 sha;
// We have checked already that the DUID and FQDN aren't empty
// so it is safe to assume that the data buffer is not empty.
- secure = sha.process(static_cast<const Botan::byte*>(&data[0]),
- data.size());
+ cryptolink::digest(&data[0], data.size(), cryptolink::SHA256, hash);
} catch (const std::exception& ex) {
isc_throw(isc::dhcp_ddns::DhcidRdataComputeError,
"error while generating DHCID from DUID: "
// Let's allocate the space for the identifier-type (2 bytes) and
// digest-type (1 byte). This is 3 bytes all together.
- bytes_.resize(3);
+ bytes_.resize(3 + hash.getLength());
// Leave first byte 0 and set the second byte. Those two bytes
// form the identifier-type.
bytes_[1] = identifier_type;
// Third byte is always equal to 1, which specifies SHA-256 digest type.
bytes_[2] = 1;
// Now let's append the digest.
- bytes_.insert(bytes_.end(), secure.begin(), secure.end());
+ std::memcpy(&bytes_[3], hash.getData(), hash.getLength());
}
std::ostream&
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dhcp_ddns/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
libdhcp_ddns_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libdhcp_ddns_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
libdhcp_ddns_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
-libdhcp_ddns_unittests_LDADD += ${BOTAN_LIBS} ${BOTAN_RPATH}
+libdhcp_ddns_unittests_LDADD += ${CRYPTO_LIBS} ${CRYPTO_RPATH}
libdhcp_ddns_unittests_LDADD += $(GTEST_LDADD)
endif
run_unittests_SOURCES += zone_checker_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-# We shouldn't need to include BOTAN_LIBS here, but there
+# We shouldn't need to include CRYPTO_LIBS here, but there
# is one test system where the path for GTEST_LDFLAGS contains
# an older version of botan, and somehow that version gets
# linked if we don't
-run_unittests_LDFLAGS = $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
+run_unittests_LDFLAGS = $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
run_unittests_LDADD = $(top_builddir)/src/lib/dns/libkea-dns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
-run_unittests_LDADD += $(BOTAN_LIBS) $(GTEST_LDADD)
+run_unittests_LDADD += $(CRYPTO_LIBS) $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)