diff options
author | Daniel P. Berrange <berrange@redhat.com> | 2015-07-01 18:10:32 +0100 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2015-07-08 13:11:01 +0200 |
commit | ca38a4cc9e36647437b837b346a41981fb8880cd (patch) | |
tree | bb29de3cad2138f694bbfb4485d0d989caacc759 /crypto | |
parent | 9fd72468dfe40532df7c64d35054994058106c42 (diff) | |
download | hqemu-ca38a4cc9e36647437b837b346a41981fb8880cd.zip hqemu-ca38a4cc9e36647437b837b346a41981fb8880cd.tar.gz |
crypto: introduce generic cipher API & built-in implementation
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>
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/Makefile.objs | 1 | ||||
-rw-r--r-- | crypto/cipher-builtin.c | 398 | ||||
-rw-r--r-- | crypto/cipher.c | 49 |
3 files changed, 448 insertions, 0 deletions
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; + if (!cipher) { + return; + } + + ctxt->free(cipher); + g_free(cipher); +} + + +int qcrypto_cipher_encrypt(QCryptoCipher *cipher, + const void *in, + void *out, + size_t len, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt = cipher->opaque; + + return ctxt->encrypt(cipher, in, out, len, errp); +} + + +int qcrypto_cipher_decrypt(QCryptoCipher *cipher, + const void *in, + void *out, + size_t len, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt = cipher->opaque; + + return ctxt->decrypt(cipher, in, out, len, errp); +} + + +int qcrypto_cipher_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt = cipher->opaque; + + return ctxt->setiv(cipher, iv, niv, errp); +} diff --git a/crypto/cipher.c b/crypto/cipher.c new file mode 100644 index 0000000..0944827 --- /dev/null +++ b/crypto/cipher.c @@ -0,0 +1,49 @@ +/* + * QEMU Crypto cipher 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/cipher.h" + +static size_t alg_key_len[QCRYPTO_CIPHER_ALG_LAST] = { + [QCRYPTO_CIPHER_ALG_AES_128] = 16, + [QCRYPTO_CIPHER_ALG_AES_192] = 24, + [QCRYPTO_CIPHER_ALG_AES_256] = 32, + [QCRYPTO_CIPHER_ALG_DES_RFB] = 8, +}; + +static bool +qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg, + size_t nkey, + Error **errp) +{ + if ((unsigned)alg >= QCRYPTO_CIPHER_ALG_LAST) { + error_setg(errp, "Cipher algorithm %d out of range", + alg); + return false; + } + + if (alg_key_len[alg] != nkey) { + error_setg(errp, "Cipher key length %zu should be %zu", + alg_key_len[alg], nkey); + return false; + } + return true; +} + +#include "crypto/cipher-builtin.c" |