[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PULL 05/12] crypto: introduce generic cipher API & bui
From: |
Aurelien Jarno |
Subject: |
Re: [Qemu-devel] [PULL 05/12] crypto: introduce generic cipher API & built-in implementation |
Date: |
Thu, 9 Jul 2015 16:09:03 +0200 |
User-agent: |
Mutt/1.5.23 (2014-03-12) |
On 2015-07-07 16:12, Paolo Bonzini wrote:
> From: "Daniel P. Berrange" <address@hidden>
>
> 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 <address@hidden>
> Message-Id: <address@hidden>
> Signed-off-by: Paolo Bonzini <address@hidden>
> ---
> 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
address@hidden http://www.aurel32.net
- [Qemu-devel] [PULL v2 00/12] Final changes for 2.4-rc0, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 01/12] vl: move rom_load_all after machine init done, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 03/12] crypto: move built-in AES implementation into crypto/, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 04/12] crypto: move built-in D3DES implementation into crypto/, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 02/12] crypto: introduce new module for computing hash digests, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 08/12] block: convert quorum blockdrv to use crypto APIs, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 05/12] crypto: introduce generic cipher API & built-in implementation, Paolo Bonzini, 2015/07/07
- Re: [Qemu-devel] [PULL 05/12] crypto: introduce generic cipher API & built-in implementation,
Aurelien Jarno <=
- [Qemu-devel] [PULL 07/12] crypto: add a nettle cipher implementation, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 11/12] ui: convert VNC to use generic cipher API, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 06/12] crypto: add a gcrypt cipher implementation, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 09/12] ui: convert VNC websockets to use crypto APIs, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 12/12] ossaudio: fix memory leak, Paolo Bonzini, 2015/07/07
- [Qemu-devel] [PULL 10/12] block: convert qcow/qcow2 to use generic cipher API, Paolo Bonzini, 2015/07/07