From: Aurelien Jarno <aurelien@aurel32.net>
To: Paolo Bonzini <pbonzini@redhat.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PULL 05/12] crypto: introduce generic cipher API & built-in implementation
Date: Thu, 9 Jul 2015 16:09:03 +0200 [thread overview]
Message-ID: <20150709140903.GA21010@aurel32.net> (raw)
In-Reply-To: <1436278368-13449-6-git-send-email-pbonzini@redhat.com>
On 2015-07-07 16:12, Paolo Bonzini wrote:
> From: "Daniel P. Berrange" <berrange@redhat.com>
>
> Introduce a generic cipher API and an implementation of it that
> supports only the built-in AES and DES-RFB algorithms.
>
> The test suite checks the supported algorithms + modes to
> validate that every backend implementation is actually correctly
> complying with the specs.
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> Message-Id: <1435770638-25715-5-git-send-email-berrange@redhat.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> crypto/Makefile.objs | 1 +
> crypto/cipher-builtin.c | 398 +++++++++++++++++++++++++++++++++++++++++++++
> crypto/cipher.c | 50 ++++++
> include/crypto/cipher.h | 210 ++++++++++++++++++++++++
> tests/.gitignore | 1 +
> tests/Makefile | 2 +
> tests/test-crypto-cipher.c | 290 +++++++++++++++++++++++++++++++++
> 7 files changed, 952 insertions(+)
> create mode 100644 crypto/cipher-builtin.c
> create mode 100644 crypto/cipher.c
> create mode 100644 include/crypto/cipher.h
> create mode 100644 tests/test-crypto-cipher.c
>
> diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
> index 9f70294..b050138 100644
> --- a/crypto/Makefile.objs
> +++ b/crypto/Makefile.objs
> @@ -2,3 +2,4 @@ util-obj-y += init.o
> util-obj-y += hash.o
> util-obj-y += aes.o
> util-obj-y += desrfb.o
> +util-obj-y += cipher.o
> diff --git a/crypto/cipher-builtin.c b/crypto/cipher-builtin.c
> new file mode 100644
> index 0000000..c625cb4
> --- /dev/null
> +++ b/crypto/cipher-builtin.c
> @@ -0,0 +1,398 @@
> +/*
> + * QEMU Crypto cipher built-in 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/aes.h"
> +#include "crypto/desrfb.h"
> +
> +typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES;
> +struct QCryptoCipherBuiltinAES {
> + AES_KEY encrypt_key;
> + AES_KEY decrypt_key;
> + uint8_t *iv;
> + size_t niv;
> +};
> +typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB;
> +struct QCryptoCipherBuiltinDESRFB {
> + uint8_t *key;
> + size_t nkey;
> +};
> +
> +typedef struct QCryptoCipherBuiltin QCryptoCipherBuiltin;
> +struct QCryptoCipherBuiltin {
> + union {
> + QCryptoCipherBuiltinAES aes;
> + QCryptoCipherBuiltinDESRFB desrfb;
> + } state;
> + void (*free)(QCryptoCipher *cipher);
> + int (*setiv)(QCryptoCipher *cipher,
> + const uint8_t *iv, size_t niv,
> + Error **errp);
> + int (*encrypt)(QCryptoCipher *cipher,
> + const void *in,
> + void *out,
> + size_t len,
> + Error **errp);
> + int (*decrypt)(QCryptoCipher *cipher,
> + const void *in,
> + void *out,
> + size_t len,
> + Error **errp);
> +};
> +
> +
> +static void qcrypto_cipher_free_aes(QCryptoCipher *cipher)
> +{
> + QCryptoCipherBuiltin *ctxt = cipher->opaque;
> +
> + g_free(ctxt->state.aes.iv);
> + g_free(ctxt);
> + cipher->opaque = NULL;
> +}
> +
> +
> +static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher,
> + const void *in,
> + void *out,
> + size_t len,
> + Error **errp)
> +{
> + QCryptoCipherBuiltin *ctxt = cipher->opaque;
> +
> + if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) {
> + const uint8_t *inptr = in;
> + uint8_t *outptr = out;
> + while (len) {
> + if (len > AES_BLOCK_SIZE) {
> + AES_encrypt(inptr, outptr, &ctxt->state.aes.encrypt_key);
> + inptr += AES_BLOCK_SIZE;
> + outptr += AES_BLOCK_SIZE;
> + len -= AES_BLOCK_SIZE;
> + } else {
> + uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
> + memcpy(tmp1, inptr, len);
> + /* Fill with 0 to avoid valgrind uninitialized reads */
> + memset(tmp1 + len, 0, sizeof(tmp1) - len);
> + AES_encrypt(tmp1, tmp2, &ctxt->state.aes.encrypt_key);
> + memcpy(outptr, tmp2, len);
> + len = 0;
> + }
> + }
> + } else {
> + AES_cbc_encrypt(in, out, len,
> + &ctxt->state.aes.encrypt_key,
> + ctxt->state.aes.iv, 1);
> + }
> +
> + return 0;
> +}
> +
> +
> +static int qcrypto_cipher_decrypt_aes(QCryptoCipher *cipher,
> + const void *in,
> + void *out,
> + size_t len,
> + Error **errp)
> +{
> + QCryptoCipherBuiltin *ctxt = cipher->opaque;
> +
> + if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) {
> + const uint8_t *inptr = in;
> + uint8_t *outptr = out;
> + while (len) {
> + if (len > AES_BLOCK_SIZE) {
> + AES_decrypt(inptr, outptr, &ctxt->state.aes.encrypt_key);
> + inptr += AES_BLOCK_SIZE;
> + outptr += AES_BLOCK_SIZE;
> + len -= AES_BLOCK_SIZE;
> + } else {
> + uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
> + memcpy(tmp1, inptr, len);
> + /* Fill with 0 to avoid valgrind uninitialized reads */
> + memset(tmp1 + len, 0, sizeof(tmp1) - len);
> + AES_decrypt(tmp1, tmp2, &ctxt->state.aes.encrypt_key);
> + memcpy(outptr, tmp2, len);
> + len = 0;
> + }
> + }
> + } else {
> + AES_cbc_encrypt(in, out, len,
> + &ctxt->state.aes.encrypt_key,
> + ctxt->state.aes.iv, 1);
> + }
> +
> + return 0;
> +}
> +
> +static int qcrypto_cipher_setiv_aes(QCryptoCipher *cipher,
> + const uint8_t *iv, size_t niv,
> + Error **errp)
> +{
> + QCryptoCipherBuiltin *ctxt = cipher->opaque;
> + if (niv != 16) {
> + error_setg(errp, "IV must be 16 bytes not %zu", niv);
> + return -1;
> + }
> +
> + g_free(ctxt->state.aes.iv);
> + ctxt->state.aes.iv = g_new0(uint8_t, niv);
> + memcpy(ctxt->state.aes.iv, iv, niv);
> + ctxt->state.aes.niv = niv;
> +
> + return 0;
> +}
> +
> +
> +
> +
> +static int qcrypto_cipher_init_aes(QCryptoCipher *cipher,
> + const uint8_t *key, size_t nkey,
> + Error **errp)
> +{
> + QCryptoCipherBuiltin *ctxt;
> +
> + if (cipher->mode != QCRYPTO_CIPHER_MODE_CBC &&
> + cipher->mode != QCRYPTO_CIPHER_MODE_ECB) {
> + error_setg(errp, "Unsupported cipher mode %d", cipher->mode);
> + return -1;
> + }
> +
> + ctxt = g_new0(QCryptoCipherBuiltin, 1);
> +
> + if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.encrypt_key) != 0) {
> + error_setg(errp, "Failed to set encryption key");
> + goto error;
> + }
> +
> + if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.decrypt_key) != 0) {
> + error_setg(errp, "Failed to set decryption key");
> + goto error;
> + }
> +
> + ctxt->free = qcrypto_cipher_free_aes;
> + ctxt->setiv = qcrypto_cipher_setiv_aes;
> + ctxt->encrypt = qcrypto_cipher_encrypt_aes;
> + ctxt->decrypt = qcrypto_cipher_decrypt_aes;
> +
> + cipher->opaque = ctxt;
> +
> + return 0;
> +
> + error:
> + g_free(ctxt);
> + return -1;
> +}
> +
> +
> +static void qcrypto_cipher_free_des_rfb(QCryptoCipher *cipher)
> +{
> + QCryptoCipherBuiltin *ctxt = cipher->opaque;
> +
> + g_free(ctxt->state.desrfb.key);
> + g_free(ctxt);
> + cipher->opaque = NULL;
> +}
> +
> +
> +static int qcrypto_cipher_encrypt_des_rfb(QCryptoCipher *cipher,
> + const void *in,
> + void *out,
> + size_t len,
> + Error **errp)
> +{
> + QCryptoCipherBuiltin *ctxt = cipher->opaque;
> + size_t i;
> +
> + if (len % 8) {
> + error_setg(errp, "Buffer size must be multiple of 8 not %zu",
> + len);
> + return -1;
> + }
> +
> + deskey(ctxt->state.desrfb.key, EN0);
> +
> + for (i = 0; i < len; i += 8) {
> + des((void *)in + i, out + i);
> + }
> +
> + return 0;
> +}
> +
> +
> +static int qcrypto_cipher_decrypt_des_rfb(QCryptoCipher *cipher,
> + const void *in,
> + void *out,
> + size_t len,
> + Error **errp)
> +{
> + QCryptoCipherBuiltin *ctxt = cipher->opaque;
> + size_t i;
> +
> + if (len % 8) {
> + error_setg(errp, "Buffer size must be multiple of 8 not %zu",
> + len);
> + return -1;
> + }
> +
> + deskey(ctxt->state.desrfb.key, DE1);
> +
> + for (i = 0; i < len; i += 8) {
> + des((void *)in + i, out + i);
> + }
> +
> + return 0;
> +}
> +
> +
> +static int qcrypto_cipher_setiv_des_rfb(QCryptoCipher *cipher,
> + const uint8_t *iv, size_t niv,
> + Error **errp)
> +{
> + error_setg(errp, "Setting IV is not supported");
> + return -1;
> +}
> +
> +
> +static int qcrypto_cipher_init_des_rfb(QCryptoCipher *cipher,
> + const uint8_t *key, size_t nkey,
> + Error **errp)
> +{
> + QCryptoCipherBuiltin *ctxt;
> +
> + if (cipher->mode != QCRYPTO_CIPHER_MODE_ECB) {
> + error_setg(errp, "Unsupported cipher mode %d", cipher->mode);
> + return -1;
> + }
> +
> + ctxt = g_new0(QCryptoCipherBuiltin, 1);
> +
> + ctxt->state.desrfb.key = g_new0(uint8_t, nkey);
> + memcpy(ctxt->state.desrfb.key, key, nkey);
> + ctxt->state.desrfb.nkey = nkey;
> +
> + ctxt->free = qcrypto_cipher_free_des_rfb;
> + ctxt->setiv = qcrypto_cipher_setiv_des_rfb;
> + ctxt->encrypt = qcrypto_cipher_encrypt_des_rfb;
> + ctxt->decrypt = qcrypto_cipher_decrypt_des_rfb;
> +
> + cipher->opaque = ctxt;
> +
> + return 0;
> +}
> +
> +
> +bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
> +{
> + switch (alg) {
> + case QCRYPTO_CIPHER_ALG_DES_RFB:
> + case QCRYPTO_CIPHER_ALG_AES_128:
> + case QCRYPTO_CIPHER_ALG_AES_192:
> + case QCRYPTO_CIPHER_ALG_AES_256:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +
> +QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
> + QCryptoCipherMode mode,
> + const uint8_t *key, size_t nkey,
> + Error **errp)
> +{
> + QCryptoCipher *cipher;
> +
> + cipher = g_new0(QCryptoCipher, 1);
> + cipher->alg = alg;
> + cipher->mode = mode;
> +
> + if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) {
> + goto error;
> + }
> +
> + switch (cipher->alg) {
> + case QCRYPTO_CIPHER_ALG_DES_RFB:
> + if (qcrypto_cipher_init_des_rfb(cipher, key, nkey, errp) < 0) {
> + goto error;
> + }
> + break;
> + case QCRYPTO_CIPHER_ALG_AES_128:
> + case QCRYPTO_CIPHER_ALG_AES_192:
> + case QCRYPTO_CIPHER_ALG_AES_256:
> + if (qcrypto_cipher_init_aes(cipher, key, nkey, errp) < 0) {
> + goto error;
> + }
> + break;
> + default:
> + error_setg(errp,
> + "Unsupported cipher algorithm %d", cipher->alg);
> + goto error;
> + }
> +
> + return cipher;
> +
> + error:
> + g_free(cipher);
> + return NULL;
> +}
> +
> +void qcrypto_cipher_free(QCryptoCipher *cipher)
> +{
> + QCryptoCipherBuiltin *ctxt = cipher->opaque;
The pointer is dereferenced here...
> + if (!cipher) {
> + return;
> + }
... but the test for the NULL pointer is only done here. This causes a
crash on a MIPS host and prevents to use any disk image in qcow2
format.
> + ctxt->free(cipher);
> + g_free(cipher);
> +}
--
Aurelien Jarno GPG: 4096R/1DDD8C9B
aurelien@aurel32.net http://www.aurel32.net
next prev parent reply other threads:[~2015-07-09 14:09 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-07-07 14:12 [Qemu-devel] [PULL v2 00/12] Final changes for 2.4-rc0 Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 01/12] vl: move rom_load_all after machine init done Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 02/12] crypto: introduce new module for computing hash digests Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 03/12] crypto: move built-in AES implementation into crypto/ Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 04/12] crypto: move built-in D3DES " Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 05/12] crypto: introduce generic cipher API & built-in implementation Paolo Bonzini
2015-07-09 14:09 ` Aurelien Jarno [this message]
2015-07-07 14:12 ` [Qemu-devel] [PULL 06/12] crypto: add a gcrypt cipher implementation Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 07/12] crypto: add a nettle " Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 08/12] block: convert quorum blockdrv to use crypto APIs Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 09/12] ui: convert VNC websockets " Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 10/12] block: convert qcow/qcow2 to use generic cipher API Paolo Bonzini
2015-07-09 10:17 ` Christian Borntraeger
2015-07-09 10:53 ` [Qemu-devel] qcow crypto changes crash on migration (was: [PULL 10/12] block: convert qcow/qcow2 to use generic cipher API) Christian Borntraeger
2015-07-09 11:20 ` [Qemu-devel] qcow crypto changes crash on migration Christian Borntraeger
2015-07-09 14:51 ` [Qemu-devel] qcow crypto changes crash on migration (was: [PULL 10/12] block: convert qcow/qcow2 to use generic cipher API) Aurelien Jarno
2015-07-07 14:12 ` [Qemu-devel] [PULL 11/12] ui: convert VNC to use generic cipher API Paolo Bonzini
2015-07-07 14:12 ` [Qemu-devel] [PULL 12/12] ossaudio: fix memory leak Paolo Bonzini
2015-07-08 10:48 ` [Qemu-devel] [PULL v2 00/12] Final changes for 2.4-rc0 Peter Maydell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150709140903.GA21010@aurel32.net \
--to=aurelien@aurel32.net \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.