On 2015-07-07 16:12, Paolo Bonzini wrote: > From: "Daniel P. Berrange" <berra...@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 <berra...@redhat.com> > Message-Id: <1435770638-25715-5-git-send-email-berra...@redhat.com> > Signed-off-by: Paolo Bonzini <pbonz...@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 aurel...@aurel32.net http://www.aurel32.net