diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index cc6569d2dd..495c1212ab 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -121,6 +121,7 @@ typedef enum { VIR_FROM_ACCESS = 55, /* Error from access control manager */ VIR_FROM_SYSTEMD = 56, /* Error from systemd code */ VIR_FROM_BHYVE = 57, /* Error from bhyve driver */ + VIR_FROM_CRYPTO = 58, /* Error from crypto code */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST diff --git a/po/POTFILES.in b/po/POTFILES.in index bcc0ee01f9..a8a59758de 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -158,6 +158,7 @@ src/util/vircgroup.c src/util/virclosecallbacks.c src/util/vircommand.c src/util/virconf.c +src/util/vircrypto.c src/util/virdbus.c src/util/virdnsmasq.c src/util/vireventpoll.c diff --git a/src/Makefile.am b/src/Makefile.am index 25d0370f0f..4bc2df4bc4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -93,6 +93,7 @@ UTIL_SOURCES = \ util/virclosecallbacks.c util/virclosecallbacks.h \ util/vircommand.c util/vircommand.h \ util/virconf.c util/virconf.h \ + util/vircrypto.c util/vircrypto.h \ util/virdbus.c util/virdbus.h util/virdbuspriv.h \ util/virdnsmasq.c util/virdnsmasq.h \ util/virebtables.c util/virebtables.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 80070c5ea2..00e3d9cd23 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1149,6 +1149,10 @@ virConfWriteFile; virConfWriteMem; +# util/vircrypto.h +virCryptoHashString; + + # util/virdbus.h virDBusCallMethod; virDBusCloseSystemBus; diff --git a/src/util/vircrypto.c b/src/util/vircrypto.c new file mode 100644 index 0000000000..3af3aa3bb0 --- /dev/null +++ b/src/util/vircrypto.c @@ -0,0 +1,79 @@ +/* + * vircrypto.c: cryptographic helper APIs + * + * Copyright (C) 2014 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.1 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 + * . + */ + +#include + +#include "vircrypto.h" +#include "virerror.h" +#include "viralloc.h" + +#include "md5.h" +#include "sha256.h" + +#define VIR_FROM_THIS VIR_FROM_CRYPTO + +static const char hex[] = "0123456789abcdef"; + +struct virHashInfo { + void *(*func)(const char *buf, size_t len, void *res); + size_t hashlen; +} hashinfo[] = { + { md5_buffer, MD5_DIGEST_SIZE }, + { sha256_buffer, SHA256_DIGEST_SIZE }, +}; + +#define VIR_CRYPTO_LARGEST_DIGEST_SIZE SHA256_DIGEST_SIZE + +verify(ARRAY_CARDINALITY(hashinfo) == VIR_CRYPTO_HASH_LAST); + +int +virCryptoHashString(virCryptoHash hash, + const char *input, + char **output) +{ + unsigned char buf[VIR_CRYPTO_LARGEST_DIGEST_SIZE]; + size_t hashstrlen; + size_t i; + + if (hash >= VIR_CRYPTO_HASH_LAST) { + virReportError(VIR_ERR_INVALID_ARG, + _("Unknown crypto hash %d"), hash); + return -1; + } + + hashstrlen = (hashinfo[hash].hashlen * 2) + 1; + + if (!(hashinfo[hash].func(input, strlen(input), buf))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to compute hash of data")); + return -1; + } + + if (VIR_ALLOC_N(*output, hashstrlen) < 0) + return -1; + + for (i = 0; i < hashinfo[hash].hashlen; i++) { + (*output)[i * 2] = hex[(buf[i] >> 4) & 0xf]; + (*output)[(i * 2) + 1] = hex[buf[i] & 0xf]; + } + (*output)[hashstrlen] = '\0'; + + return 0; +} diff --git a/src/util/vircrypto.h b/src/util/vircrypto.h new file mode 100644 index 0000000000..f67d49da47 --- /dev/null +++ b/src/util/vircrypto.h @@ -0,0 +1,40 @@ +/* + * vircrypto.h: cryptographic helper APIs + * + * Copyright (C) 2014 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.1 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 + * . + */ + +#ifndef __VIR_CRYPTO_H__ +# define __VIR_CRYPTO_H__ + +# include "internal.h" + +typedef enum { + VIR_CRYPTO_HASH_MD5, /* Don't use this except for historic compat */ + VIR_CRYPTO_HASH_SHA256, + + VIR_CRYPTO_HASH_LAST +} virCryptoHash; + +int +virCryptoHashString(virCryptoHash hash, + const char *input, + char **output) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) + ATTRIBUTE_RETURN_CHECK; + +#endif /* __VIR_CRYPTO_H__ */ diff --git a/src/util/virerror.c b/src/util/virerror.c index 820e1adbac..3eb4f5d267 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -125,6 +125,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Access Manager", /* 55 */ "Systemd", "Bhyve", + "Crypto", ) diff --git a/tests/Makefile.am b/tests/Makefile.am index acc9e268f9..5c1f420e9e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -135,6 +135,7 @@ test_programs = virshtest sockettest \ virauthconfigtest \ virbitmaptest \ vircgrouptest \ + vircryptotest \ virpcitest \ virendiantest \ virfiletest \ @@ -816,6 +817,10 @@ vircgroupmock_la_CFLAGS = $(AM_CFLAGS) vircgroupmock_la_LDFLAGS = -module -avoid-version \ -rpath /evil/libtool/hack/to/force/shared/lib/creation +vircryptotest_SOURCES = \ + vircryptotest.c testutils.h testutils.c +vircryptotest_LDADD = $(LDADDS) + virpcitest_SOURCES = \ virpcitest.c testutils.h testutils.c virpcitest_LDADD = $(LDADDS) diff --git a/tests/vircryptotest.c b/tests/vircryptotest.c new file mode 100644 index 0000000000..bfc47db7fc --- /dev/null +++ b/tests/vircryptotest.c @@ -0,0 +1,90 @@ +/* + * vircryptotest.c: cryptographic helper test suite + * + * Copyright (C) 2014 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.1 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 + * . + */ + +#include + +#include "vircrypto.h" + +#include "testutils.h" + + +struct testCryptoHashData { + virCryptoHash hash; + const char *input; + const char *output; +}; + +static int +testCryptoHash(const void *opaque) +{ + const struct testCryptoHashData *data = opaque; + char *actual = NULL; + int ret = -1; + + if (virCryptoHashString(data->hash, data->input, &actual) < 0) { + fprintf(stderr, "Failed to generate crypto hash\n"); + goto cleanup; + } + + if (STRNEQ_NULLABLE(data->output, actual)) { + fprintf(stderr, "Expected hash '%s' but got '%s'\n", + data->output, NULLSTR(actual)); + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FREE(actual); + return ret; +} + + +static int +mymain(void) +{ + int ret = 0; + +#define VIR_CRYPTO_HASH(h, i, o) \ + do { \ + struct testCryptoHashData data = { \ + .hash = h, \ + .input = i, \ + .output = o, \ + }; \ + if (virtTestRun("Hash " i, testCryptoHash, &data) < 0) \ + ret = -1; \ + } while (0) + + VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_MD5, "", "d41d8cd98f00b204e9800998ecf8427e"); + VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_SHA256, "", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + + VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_MD5, " ", "7215ee9c7d9dc229d2921a40e899ec5f"); + VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_SHA256, " ", "36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068"); + + VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_MD5, "\n", "68b329da9893e34099c7d8ad5cb9c940"); + VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_SHA256, "\n", "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"); + + VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_MD5, "The quick brown fox", "a2004f37730b9445670a738fa0fc9ee5"); + VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_SHA256, "The quick brown fox", "5cac4f980fedc3d3f1f99b4be3472c9b30d56523e632d151237ec9309048bda9"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain)