/*
* vircrypto.c: cryptographic helper APIs
*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014, 2016 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
#include <config.h>
#include "vircrypto.h"
+#include "virlog.h"
#include "virerror.h"
#include "viralloc.h"
#include "md5.h"
#include "sha256.h"
+#ifdef WITH_GNUTLS
+# include <gnutls/gnutls.h>
+# if HAVE_GNUTLS_CRYPTO_H
+# include <gnutls/crypto.h>
+# endif
+#endif
+
+VIR_LOG_INIT("util.crypto");
#define VIR_FROM_THIS VIR_FROM_CRYPTO
return 0;
}
+
+
+/* virCryptoHaveCipher:
+ * @algorithm: Specific cipher algorithm desired
+ *
+ * Expected to be called prior to virCryptoEncryptData in order
+ * to determine whether the requested encryption option is available,
+ * so that "other" alternatives can be taken if the algorithm is
+ * not available.
+ *
+ * Returns true if we can support the encryption.
+ */
+bool
+virCryptoHaveCipher(virCryptoCipher algorithm)
+{
+ switch (algorithm) {
+
+ case VIR_CRYPTO_CIPHER_AES256CBC:
+#ifdef HAVE_GNUTLS_CIPHER_ENCRYPT
+ return true;
+#else
+ return false;
+#endif
+
+ case VIR_CRYPTO_CIPHER_NONE:
+ case VIR_CRYPTO_CIPHER_LAST:
+ break;
+ };
+
+ return false;
+}
+
+
+#ifdef HAVE_GNUTLS_CIPHER_ENCRYPT
+/* virCryptoEncryptDataAESgntuls:
+ *
+ * Performs the AES gnutls encryption
+ *
+ * Same input as virCryptoEncryptData, except the algoritm is replaced
+ * by the specific gnutls algorithm.
+ *
+ * Encrypts the @data buffer using the @enckey and if available the @iv
+ *
+ * Returns 0 on success with the ciphertext being filled. It is the
+ * caller's responsibility to clear and free it. Returns -1 on failure
+ * w/ error set.
+ */
+static int
+virCryptoEncryptDataAESgnutls(gnutls_cipher_algorithm_t gnutls_enc_alg,
+ uint8_t *enckey,
+ size_t enckeylen,
+ uint8_t *iv,
+ size_t ivlen,
+ uint8_t *data,
+ size_t datalen,
+ uint8_t **ciphertextret,
+ size_t *ciphertextlenret)
+{
+ int rc;
+ size_t i;
+ gnutls_cipher_hd_t handle = NULL;
+ gnutls_datum_t enc_key;
+ gnutls_datum_t iv_buf;
+ uint8_t *ciphertext;
+ size_t ciphertextlen;
+
+ /* Allocate a padded buffer, copy in the data */
+ ciphertextlen = VIR_ROUND_UP(datalen, 16);
+ if (VIR_ALLOC_N(ciphertext, ciphertextlen) < 0)
+ return -1;
+ memcpy(ciphertext, data, datalen);
+
+ /* Fill in the padding of the buffer with the size of the padding
+ * which is required for decryption. */
+ for (i = datalen; i < ciphertextlen; i++)
+ ciphertext[i] = ciphertextlen - datalen;
+
+ /* Initialize the gnutls cipher */
+ enc_key.size = enckeylen;
+ enc_key.data = enckey;
+ if (iv) {
+ iv_buf.size = ivlen;
+ iv_buf.data = iv;
+ }
+ if ((rc = gnutls_cipher_init(&handle, gnutls_enc_alg,
+ &enc_key, &iv_buf)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to initialize cipher: '%s'"),
+ gnutls_strerror(rc));
+ goto error;
+ }
+
+ /* Encrypt the data and free the memory for cipher operations */
+ rc = gnutls_cipher_encrypt(handle, ciphertext, ciphertextlen);
+ gnutls_cipher_deinit(handle);
+ memset(&enc_key, 0, sizeof(gnutls_datum_t));
+ memset(&iv_buf, 0, sizeof(gnutls_datum_t));
+ if (rc < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to encrypt the data: '%s'"),
+ gnutls_strerror(rc));
+ goto error;
+ }
+
+ *ciphertextret = ciphertext;
+ *ciphertextlenret = ciphertextlen;
+ return 0;
+
+ error:
+ VIR_DISPOSE_N(ciphertext, ciphertextlen);
+ memset(&enc_key, 0, sizeof(gnutls_datum_t));
+ memset(&iv_buf, 0, sizeof(gnutls_datum_t));
+ return -1;
+}
+#endif
+
+
+/* virCryptoEncryptData:
+ * @algorithm: algoritm desired for encryption
+ * @enckey: encryption key
+ * @enckeylen: encription key length
+ * @iv: initialization vector
+ * @ivlen: length of initialization vector
+ * @data: data to encrypt
+ * @datalen: length of data
+ * @ciphertext: stream of bytes allocated to store ciphertext
+ * @ciphertextlen: size of the stream of bytes
+ *
+ * If available, attempt and return the requested encryption type
+ * using the parameters passed.
+ *
+ * Returns 0 on success, -1 on failure with error set
+ */
+int
+virCryptoEncryptData(virCryptoCipher algorithm,
+ uint8_t *enckey,
+ size_t enckeylen,
+ uint8_t *iv,
+ size_t ivlen,
+ uint8_t *data,
+ size_t datalen,
+ uint8_t **ciphertext,
+ size_t *ciphertextlen)
+{
+ switch (algorithm) {
+ case VIR_CRYPTO_CIPHER_AES256CBC:
+ if (enckeylen != 32) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("AES256CBC encryption invalid keylen=%zu"),
+ enckeylen);
+ return -1;
+ }
+
+ if (ivlen != 16) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("AES256CBC initialization vector invalid len=%zu"),
+ ivlen);
+ return -1;
+ }
+
+#ifdef HAVE_GNUTLS_CIPHER_ENCRYPT
+ /*
+ * Encrypt the data buffer using an encryption key and
+ * initialization vector via the gnutls_cipher_encrypt API
+ * for GNUTLS_CIPHER_AES_256_CBC.
+ */
+ return virCryptoEncryptDataAESgnutls(GNUTLS_CIPHER_AES_256_CBC,
+ enckey, enckeylen, iv, ivlen,
+ data, datalen,
+ ciphertext, ciphertextlen);
+#else
+ break;
+#endif
+
+ case VIR_CRYPTO_CIPHER_NONE:
+ case VIR_CRYPTO_CIPHER_LAST:
+ break;
+ }
+
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("algorithm=%d is not supported"), algorithm);
+ return -1;
+}
/*
* vircrypto.h: cryptographic helper APIs
*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014, 2016 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
VIR_CRYPTO_HASH_LAST
} virCryptoHash;
+
+typedef enum {
+ VIR_CRYPTO_CIPHER_NONE = 0,
+ VIR_CRYPTO_CIPHER_AES256CBC,
+
+ VIR_CRYPTO_CIPHER_LAST
+} virCryptoCipher;
+
int
virCryptoHashString(virCryptoHash hash,
const char *input,
ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_RETURN_CHECK;
+bool virCryptoHaveCipher(virCryptoCipher algorithm);
+
+int virCryptoEncryptData(virCryptoCipher algorithm,
+ uint8_t *enckey, size_t enckeylen,
+ uint8_t *iv, size_t ivlen,
+ uint8_t *data, size_t datalen,
+ uint8_t **ciphertext, size_t *ciphertextlen)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(6)
+ ATTRIBUTE_NONNULL(8) ATTRIBUTE_NONNULL(9) ATTRIBUTE_RETURN_CHECK;
+
#endif /* __VIR_CRYPTO_H__ */
#include <config.h>
#include "vircrypto.h"
+#include "virrandom.h"
#include "testutils.h"
+#define VIR_FROM_THIS VIR_FROM_NONE
struct testCryptoHashData {
virCryptoHash hash;
}
+struct testCryptoEncryptData {
+ virCryptoCipher algorithm;
+ uint8_t *input;
+ size_t inputlen;
+ uint8_t *ciphertext;
+ size_t ciphertextlen;
+};
+
+static int
+testCryptoEncrypt(const void *opaque)
+{
+ const struct testCryptoEncryptData *data = opaque;
+ uint8_t *enckey = NULL;
+ size_t enckeylen = 32;
+ uint8_t *iv = NULL;
+ size_t ivlen = 16;
+ uint8_t *ciphertext = NULL;
+ size_t ciphertextlen = 0;
+ int ret = -1;
+
+ if (!virCryptoHaveCipher(data->algorithm)) {
+ fprintf(stderr, "cipher algorithm=%d unavailable\n", data->algorithm);
+ return EXIT_AM_SKIP;
+ }
+
+ if (VIR_ALLOC_N(enckey, enckeylen) < 0 ||
+ VIR_ALLOC_N(iv, ivlen) < 0)
+ goto cleanup;
+
+ if (virRandomBytes(enckey, enckeylen) < 0 ||
+ virRandomBytes(iv, ivlen) < 0)
+ goto cleanup;
+
+ if (virCryptoEncryptData(data->algorithm, enckey, enckeylen, iv, ivlen,
+ data->input, data->inputlen,
+ &ciphertext, &ciphertextlen) < 0)
+ goto cleanup;
+
+ if (data->ciphertextlen != ciphertextlen) {
+ fprintf(stderr, "Expected ciphertextlen(%zu) doesn't match (%zu)\n",
+ data->ciphertextlen, ciphertextlen);
+ goto cleanup;
+ }
+
+ if (memcmp(data->ciphertext, ciphertext, ciphertextlen)) {
+ fprintf(stderr, "Expected ciphertext doesn't match\n");
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(enckey);
+ VIR_FREE(iv);
+ VIR_FREE(ciphertext);
+
+ return ret;
+}
+
+
static int
mymain(void)
{
int ret = 0;
+ uint8_t secretdata[8];
+ uint8_t expected_ciphertext[16] = {0x48, 0x8e, 0x9, 0xb9,
+ 0x6a, 0xa6, 0x24, 0x5f,
+ 0x1b, 0x8c, 0x3f, 0x48,
+ 0x27, 0xae, 0xb6, 0x7a};
#define VIR_CRYPTO_HASH(h, i, o) \
do { \
VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_MD5, "The quick brown fox", "a2004f37730b9445670a738fa0fc9ee5");
VIR_CRYPTO_HASH(VIR_CRYPTO_HASH_SHA256, "The quick brown fox", "5cac4f980fedc3d3f1f99b4be3472c9b30d56523e632d151237ec9309048bda9");
+#undef VIR_CRYPTO_HASH
+
+#define VIR_CRYPTO_ENCRYPT(a, n, i, il, c, cl) \
+ do { \
+ struct testCryptoEncryptData data = { \
+ .algorithm = a, \
+ .input = i, \
+ .inputlen = il, \
+ .ciphertext = c, \
+ .ciphertextlen = cl, \
+ }; \
+ if (virtTestRun("Encrypt " n, testCryptoEncrypt, &data) < 0) \
+ ret = -1; \
+ } while (0)
+
+ memset(&secretdata, 0, 8);
+ memcpy(&secretdata, "letmein", 7);
+
+ VIR_CRYPTO_ENCRYPT(VIR_CRYPTO_CIPHER_AES256CBC, "aes265cbc",
+ secretdata, 7, expected_ciphertext, 16);
+
+#undef VIR_CRYPTO_ENCRYPT
+
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
-VIRT_TEST_MAIN(mymain)
+/* Forces usage of not so random virRandomBytes */
+VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virrandommock.so")