]> xenbits.xensource.com Git - libvirt.git/commitdiff
util: Introduce encryption APIs
authorJohn Ferlan <jferlan@redhat.com>
Wed, 18 May 2016 15:29:06 +0000 (11:29 -0400)
committerJohn Ferlan <jferlan@redhat.com>
Fri, 20 May 2016 15:09:01 +0000 (11:09 -0400)
Introduce virCryptoHaveCipher and virCryptoEncryptData to handle
performing encryption.

 virCryptoHaveCipher:
   Boolean function to determine whether the requested cipher algorithm
   is available. It's expected this API will be called prior to
   virCryptoEncryptdata. It will return true/false.

 virCryptoEncryptData:
   Based on the requested cipher type, call the specific encryption
   API to encrypt the data.

Currently the only algorithm support is the AES 256 CBC encryption.

Adjust tests for the API's

configure.ac
src/libvirt_private.syms
src/util/vircrypto.c
src/util/vircrypto.h
tests/vircryptotest.c

index 378069d3e9753abbd235bcbcc80bafeeea889bcd..10fbd20e65d36a21091f93c5aafe4092c23eeec9 100644 (file)
@@ -1261,6 +1261,7 @@ if test "x$with_gnutls" != "xno"; then
   ]])
 
   AC_CHECK_FUNCS([gnutls_rnd])
+  AC_CHECK_FUNCS([gnutls_cipher_encrypt])
 
   CFLAGS="$old_CFLAGS"
   LIBS="$old_LIBS"
index 868bcfa2ddce4c7f741ad740e1415ceb841775b0..6c02b10eff4e8f05b5244cb5542723984b5a2fb8 100644 (file)
@@ -1394,7 +1394,9 @@ virConfWriteMem;
 
 
 # util/vircrypto.h
+virCryptoEncryptData;
 virCryptoHashString;
+virCryptoHaveCipher;
 
 
 # util/virdbus.h
index 39a479af48014d34c733e41625a433593520443a..ed8675d96f836949d15e8483511676ffd4cc5d97 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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
 
@@ -76,3 +85,186 @@ virCryptoHashString(virCryptoHash hash,
 
     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;
+}
index f67d49da47b68e804c6f9ab2c2342693886c1061..5d6d37c497f79f37802b0a7c3709c3a93fc778cc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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
@@ -30,6 +30,14 @@ typedef enum {
     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,
@@ -37,4 +45,14 @@ virCryptoHashString(virCryptoHash hash,
     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__ */
index bfc47db7fc830fcd56b5ca9b73e928381ebb6759..72265d949a44a6ee7f25456d6213a2cda7e52945 100644 (file)
 #include <config.h>
 
 #include "vircrypto.h"
+#include "virrandom.h"
 
 #include "testutils.h"
 
+#define VIR_FROM_THIS VIR_FROM_NONE
 
 struct testCryptoHashData {
     virCryptoHash hash;
@@ -56,10 +58,74 @@ testCryptoHash(const void *opaque)
 }
 
 
+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 {                                        \
@@ -84,7 +150,31 @@ mymain(void)
     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")