Skip to content

Commit

Permalink
crypto: introduce new module for computing hash digests
Browse files Browse the repository at this point in the history
Introduce a new crypto/ directory that will (eventually) contain
all the cryptographic related code. This initially defines a
wrapper for initializing gnutls and for computing hashes with
gnutls. The former ensures that gnutls is guaranteed to be
initialized exactly once in QEMU regardless of CLI args. The
block quorum code currently fails to initialize gnutls so it
only works by luck, if VNC server TLS is not requested. The
hash APIs avoids the need to litter the rest of the code with
preprocessor checks and simplifies callers by allocating the
correct amount of memory for the requested hash.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Message-Id: <1435770638-25715-2-git-send-email-berrange@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
berrange authored and bonzini committed Jul 7, 2015
1 parent 6b3f7f6 commit ddbb0d0
Show file tree
Hide file tree
Showing 12 changed files with 752 additions and 0 deletions.
7 changes: 7 additions & 0 deletions MAINTAINERS
Expand Up @@ -1052,6 +1052,13 @@ S: Supported
F: qemu-seccomp.c
F: include/sysemu/seccomp.h

Cryptography
M: Daniel P. Berrange <berrange@redhat.com>
S: Maintained
F: crypto/
F: include/crypto/
F: tests/test-crypto-*

Usermode Emulation
------------------
Overall
Expand Down
1 change: 1 addition & 0 deletions Makefile.objs
Expand Up @@ -2,6 +2,7 @@
# Common libraries for tools and emulators
stub-obj-y = stubs/
util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
util-obj-y += crypto/

#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
Expand Down
45 changes: 45 additions & 0 deletions configure
Expand Up @@ -330,6 +330,8 @@ glusterfs_zerofill="no"
archipelago="no"
gtk=""
gtkabi=""
gnutls=""
gnutls_hash=""
vte=""
tpm="yes"
libssh2=""
Expand Down Expand Up @@ -1118,6 +1120,10 @@ for opt do
;;
--enable-gtk) gtk="yes"
;;
--disable-gnutls) gnutls="no"
;;
--enable-gnutls) gnutls="yes"
;;
--enable-rdma) rdma="yes"
;;
--disable-rdma) rdma="no"
Expand Down Expand Up @@ -1328,6 +1334,7 @@ disabled with --disable-FEATURE, default is enabled if available:
debug-info debugging information
sparse sparse checker
gnutls GNUTLS cryptography support
sdl SDL UI
--with-sdlabi select preferred SDL ABI 1.2 or 2.0
gtk gtk UI
Expand Down Expand Up @@ -2115,6 +2122,36 @@ if test "$gtk" != "no"; then
fi
fi


##########################################
# GNUTLS probe

if test "$gnutls" != "no"; then
if $pkg_config --exists "gnutls"; then
gnutls_cflags=`$pkg_config --cflags gnutls`
gnutls_libs=`$pkg_config --libs gnutls`
libs_softmmu="$gnutls_libs $libs_softmmu"
libs_tools="$gnutls_libs $libs_tools"
QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags"
gnutls="yes"

# gnutls_hash_init requires >= 2.9.10
if $pkg_config --exists "gnutls >= 2.9.10"; then
gnutls_hash="yes"
else
gnutls_hash="no"
fi
elif test "$gnutls" = "yes"; then
feature_not_found "gnutls" "Install gnutls devel"
else
gnutls="no"
gnutls_hash="no"
fi
else
gnutls_hash="no"
fi


##########################################
# VTE probe

Expand Down Expand Up @@ -4424,6 +4461,8 @@ fi
echo "pixman $pixman"
echo "SDL support $sdl"
echo "GTK support $gtk"
echo "GNUTLS support $gnutls"
echo "GNUTLS hash $gnutls_hash"
echo "VTE support $vte"
echo "curses support $curses"
echo "curl support $curl"
Expand Down Expand Up @@ -4782,6 +4821,12 @@ if test "$gtk" = "yes" ; then
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
fi
if test "$gnutls" = "yes" ; then
echo "CONFIG_GNUTLS=y" >> $config_host_mak
fi
if test "$gnutls_hash" = "yes" ; then
echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak
fi
if test "$vte" = "yes" ; then
echo "CONFIG_VTE=y" >> $config_host_mak
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
Expand Down
2 changes: 2 additions & 0 deletions crypto/Makefile.objs
@@ -0,0 +1,2 @@
util-obj-y += init.o
util-obj-y += hash.o
200 changes: 200 additions & 0 deletions crypto/hash.c
@@ -0,0 +1,200 @@
/*
* QEMU Crypto hash algorithms
*
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/

#include "crypto/hash.h"

#ifdef CONFIG_GNUTLS_HASH
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>

static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = {
[QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
[QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
[QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
};

gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
{
if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map)) {
return true;
}
return false;
}

int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
const struct iovec *iov,
size_t niov,
uint8_t **result,
size_t *resultlen,
Error **errp)
{
int i, ret;
gnutls_hash_hd_t dig;

if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
}

ret = gnutls_hash_init(&dig, qcrypto_hash_alg_map[alg]);

if (ret < 0) {
error_setg(errp,
"Unable to initialize hash algorithm: %s",
gnutls_strerror(ret));
return -1;
}

for (i = 0; i < niov; i++) {
ret = gnutls_hash(dig, iov[i].iov_base, iov[i].iov_len);
if (ret < 0) {
error_setg(errp,
"Unable process hash data: %s",
gnutls_strerror(ret));
goto error;
}
}

ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]);
if (ret <= 0) {
error_setg(errp,
"Unable to get hash length: %s",
gnutls_strerror(ret));
goto error;
}
if (*resultlen == 0) {
*resultlen = ret;
*result = g_new0(uint8_t, *resultlen);
} else if (*resultlen != ret) {
error_setg(errp,
"Result buffer size %zu is smaller than hash %d",
*resultlen, ret);
goto error;
}

gnutls_hash_deinit(dig, *result);
return 0;

error:
gnutls_hash_deinit(dig, NULL);
return -1;
}

#else /* ! CONFIG_GNUTLS_HASH */

gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED)
{
return false;
}

int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
const struct iovec *iov G_GNUC_UNUSED,
size_t niov G_GNUC_UNUSED,
uint8_t **result G_GNUC_UNUSED,
size_t *resultlen G_GNUC_UNUSED,
Error **errp)
{
error_setg(errp,
"Hash algorithm %d not supported without GNUTLS",
alg);
return -1;
}

#endif /* ! CONFIG_GNUTLS_HASH */

int qcrypto_hash_bytes(QCryptoHashAlgorithm alg,
const char *buf,
size_t len,
uint8_t **result,
size_t *resultlen,
Error **errp)
{
struct iovec iov = { .iov_base = (char *)buf,
.iov_len = len };
return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp);
}

static const char hex[] = "0123456789abcdef";

int qcrypto_hash_digestv(QCryptoHashAlgorithm alg,
const struct iovec *iov,
size_t niov,
char **digest,
Error **errp)
{
uint8_t *result = NULL;
size_t resultlen = 0;
size_t i;

if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
return -1;
}

*digest = g_new0(char, (resultlen * 2) + 1);
for (i = 0 ; i < resultlen ; i++) {
(*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf];
(*digest)[(i * 2) + 1] = hex[result[i] & 0xf];
}
(*digest)[resultlen * 2] = '\0';
g_free(result);
return 0;
}

int qcrypto_hash_digest(QCryptoHashAlgorithm alg,
const char *buf,
size_t len,
char **digest,
Error **errp)
{
struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };

return qcrypto_hash_digestv(alg, &iov, 1, digest, errp);
}

int qcrypto_hash_base64v(QCryptoHashAlgorithm alg,
const struct iovec *iov,
size_t niov,
char **base64,
Error **errp)
{
uint8_t *result = NULL;
size_t resultlen = 0;

if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
return -1;
}

*base64 = g_base64_encode(result, resultlen);
g_free(result);
return 0;
}

int qcrypto_hash_base64(QCryptoHashAlgorithm alg,
const char *buf,
size_t len,
char **base64,
Error **errp)
{
struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };

return qcrypto_hash_base64v(alg, &iov, 1, base64, errp);
}
60 changes: 60 additions & 0 deletions crypto/init.c
@@ -0,0 +1,60 @@
/*
* QEMU Crypto initialization
*
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/

#include "crypto/init.h"

#ifdef CONFIG_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>

/* #define DEBUG_GNUTLS */

#ifdef DEBUG_GNUTLS
static void qcrypto_gnutls_log(int level, const char *str)
{
fprintf(stderr, "%d: %s", level, str);
}
#endif

int qcrypto_init(Error **errp)
{
int ret;
ret = gnutls_global_init();
if (ret < 0) {
error_setg(errp,
"Unable to initialize GNUTLS library: %s",
gnutls_strerror(ret));
return -1;
}
#ifdef DEBUG_GNUTLS
gnutls_global_set_log_level(10);
gnutls_global_set_log_function(qcrypto_gnutls_log);
#endif
return 0;
}

#else /* ! CONFIG_GNUTLS */

int qcrypto_init(Error **errp G_GNUC_UNUSED)
{
return 0;
}

#endif /* ! CONFIG_GNUTLS */

0 comments on commit ddbb0d0

Please sign in to comment.