diff options
Diffstat (limited to 'crypto/cipher-builtin.c.inc')
-rw-r--r-- | crypto/cipher-builtin.c.inc | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/crypto/cipher-builtin.c.inc b/crypto/cipher-builtin.c.inc new file mode 100644 index 0000000000..6eafd39da0 --- /dev/null +++ b/crypto/cipher-builtin.c.inc @@ -0,0 +1,532 @@ +/* + * 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.1 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 "qemu/osdep.h" +#include "crypto/aes.h" +#include "crypto/desrfb.h" +#include "crypto/xts.h" +#include "cipherpriv.h" + +typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext; +struct QCryptoCipherBuiltinAESContext { + AES_KEY enc; + AES_KEY dec; +}; +typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES; +struct QCryptoCipherBuiltinAES { + QCryptoCipherBuiltinAESContext key; + QCryptoCipherBuiltinAESContext key_tweak; + uint8_t iv[AES_BLOCK_SIZE]; +}; +typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB; +struct QCryptoCipherBuiltinDESRFB { + uint8_t *key; + size_t nkey; +}; + +typedef struct QCryptoCipherBuiltin QCryptoCipherBuiltin; +struct QCryptoCipherBuiltin { + union { + QCryptoCipherBuiltinAES aes; + QCryptoCipherBuiltinDESRFB desrfb; + } state; + size_t blocksize; + 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); + cipher->opaque = NULL; +} + + +static void qcrypto_cipher_aes_ecb_encrypt(const AES_KEY *key, + const void *in, + void *out, + size_t len) +{ + const uint8_t *inptr = in; + uint8_t *outptr = out; + while (len) { + if (len > AES_BLOCK_SIZE) { + AES_encrypt(inptr, outptr, 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, key); + memcpy(outptr, tmp2, len); + len = 0; + } + } +} + + +static void qcrypto_cipher_aes_ecb_decrypt(const AES_KEY *key, + const void *in, + void *out, + size_t len) +{ + const uint8_t *inptr = in; + uint8_t *outptr = out; + while (len) { + if (len > AES_BLOCK_SIZE) { + AES_decrypt(inptr, outptr, 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, key); + memcpy(outptr, tmp2, len); + len = 0; + } + } +} + + +static void qcrypto_cipher_aes_xts_encrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const QCryptoCipherBuiltinAESContext *aesctx = ctx; + + qcrypto_cipher_aes_ecb_encrypt(&aesctx->enc, src, dst, length); +} + + +static void qcrypto_cipher_aes_xts_decrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const QCryptoCipherBuiltinAESContext *aesctx = ctx; + + qcrypto_cipher_aes_ecb_decrypt(&aesctx->dec, src, dst, length); +} + + +static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher, + const void *in, + void *out, + size_t len, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt = cipher->opaque; + + switch (cipher->mode) { + case QCRYPTO_CIPHER_MODE_ECB: + qcrypto_cipher_aes_ecb_encrypt(&ctxt->state.aes.key.enc, + in, out, len); + break; + case QCRYPTO_CIPHER_MODE_CBC: + AES_cbc_encrypt(in, out, len, + &ctxt->state.aes.key.enc, + ctxt->state.aes.iv, 1); + break; + case QCRYPTO_CIPHER_MODE_XTS: + xts_encrypt(&ctxt->state.aes.key, + &ctxt->state.aes.key_tweak, + qcrypto_cipher_aes_xts_encrypt, + qcrypto_cipher_aes_xts_decrypt, + ctxt->state.aes.iv, + len, out, in); + break; + default: + g_assert_not_reached(); + } + + return 0; +} + + +static int qcrypto_cipher_decrypt_aes(QCryptoCipher *cipher, + const void *in, + void *out, + size_t len, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt = cipher->opaque; + + switch (cipher->mode) { + case QCRYPTO_CIPHER_MODE_ECB: + qcrypto_cipher_aes_ecb_decrypt(&ctxt->state.aes.key.dec, + in, out, len); + break; + case QCRYPTO_CIPHER_MODE_CBC: + AES_cbc_encrypt(in, out, len, + &ctxt->state.aes.key.dec, + ctxt->state.aes.iv, 0); + break; + case QCRYPTO_CIPHER_MODE_XTS: + xts_decrypt(&ctxt->state.aes.key, + &ctxt->state.aes.key_tweak, + qcrypto_cipher_aes_xts_encrypt, + qcrypto_cipher_aes_xts_decrypt, + ctxt->state.aes.iv, + len, out, in); + break; + default: + g_assert_not_reached(); + } + + 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 != AES_BLOCK_SIZE) { + error_setg(errp, "IV must be %d bytes not %zu", + AES_BLOCK_SIZE, niv); + return -1; + } + + memcpy(ctxt->state.aes.iv, iv, AES_BLOCK_SIZE); + + return 0; +} + + + + +static QCryptoCipherBuiltin * +qcrypto_cipher_init_aes(QCryptoCipherMode mode, + const uint8_t *key, size_t nkey, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt; + + if (mode != QCRYPTO_CIPHER_MODE_CBC && + mode != QCRYPTO_CIPHER_MODE_ECB && + mode != QCRYPTO_CIPHER_MODE_XTS) { + error_setg(errp, "Unsupported cipher mode %s", + QCryptoCipherMode_str(mode)); + return NULL; + } + + ctxt = g_new0(QCryptoCipherBuiltin, 1); + + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + if (AES_set_encrypt_key(key, nkey * 4, &ctxt->state.aes.key.enc) != 0) { + error_setg(errp, "Failed to set encryption key"); + goto error; + } + + if (AES_set_decrypt_key(key, nkey * 4, &ctxt->state.aes.key.dec) != 0) { + error_setg(errp, "Failed to set decryption key"); + goto error; + } + + if (AES_set_encrypt_key(key + (nkey / 2), nkey * 4, + &ctxt->state.aes.key_tweak.enc) != 0) { + error_setg(errp, "Failed to set encryption key"); + goto error; + } + + if (AES_set_decrypt_key(key + (nkey / 2), nkey * 4, + &ctxt->state.aes.key_tweak.dec) != 0) { + error_setg(errp, "Failed to set decryption key"); + goto error; + } + } else { + if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.key.enc) != 0) { + error_setg(errp, "Failed to set encryption key"); + goto error; + } + + if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.key.dec) != 0) { + error_setg(errp, "Failed to set decryption key"); + goto error; + } + } + + ctxt->blocksize = AES_BLOCK_SIZE; + ctxt->free = qcrypto_cipher_free_aes; + ctxt->setiv = qcrypto_cipher_setiv_aes; + ctxt->encrypt = qcrypto_cipher_encrypt_aes; + ctxt->decrypt = qcrypto_cipher_decrypt_aes; + + return ctxt; + + error: + g_free(ctxt); + return NULL; +} + + +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 QCryptoCipherBuiltin * +qcrypto_cipher_init_des_rfb(QCryptoCipherMode mode, + const uint8_t *key, size_t nkey, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt; + + if (mode != QCRYPTO_CIPHER_MODE_ECB) { + error_setg(errp, "Unsupported cipher mode %s", + QCryptoCipherMode_str(mode)); + return NULL; + } + + 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->blocksize = 8; + 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; + + return ctxt; +} + + +bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode) +{ + 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: + break; + default: + return false; + } + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + case QCRYPTO_CIPHER_MODE_CBC: + case QCRYPTO_CIPHER_MODE_XTS: + return true; + case QCRYPTO_CIPHER_MODE_CTR: + return false; + default: + return false; + } +} + + +static QCryptoCipherBuiltin *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode, + const uint8_t *key, + size_t nkey, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt; + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + case QCRYPTO_CIPHER_MODE_CBC: + case QCRYPTO_CIPHER_MODE_XTS: + break; + default: + error_setg(errp, "Unsupported cipher mode %s", + QCryptoCipherMode_str(mode)); + return NULL; + } + + if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { + return NULL; + } + + switch (alg) { + case QCRYPTO_CIPHER_ALG_DES_RFB: + ctxt = qcrypto_cipher_init_des_rfb(mode, key, nkey, errp); + break; + case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALG_AES_192: + case QCRYPTO_CIPHER_ALG_AES_256: + ctxt = qcrypto_cipher_init_aes(mode, key, nkey, errp); + break; + default: + error_setg(errp, + "Unsupported cipher algorithm %s", + QCryptoCipherAlgorithm_str(alg)); + return NULL; + } + + return ctxt; +} + +static void +qcrypto_builtin_cipher_ctx_free(QCryptoCipher *cipher) +{ + QCryptoCipherBuiltin *ctxt; + + ctxt = cipher->opaque; + ctxt->free(cipher); +} + + +static int +qcrypto_builtin_cipher_encrypt(QCryptoCipher *cipher, + const void *in, + void *out, + size_t len, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt = cipher->opaque; + + if (len & (ctxt->blocksize - 1)) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctxt->blocksize); + return -1; + } + + return ctxt->encrypt(cipher, in, out, len, errp); +} + + +static int +qcrypto_builtin_cipher_decrypt(QCryptoCipher *cipher, + const void *in, + void *out, + size_t len, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt = cipher->opaque; + + if (len & (ctxt->blocksize - 1)) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctxt->blocksize); + return -1; + } + + return ctxt->decrypt(cipher, in, out, len, errp); +} + + +static int +qcrypto_builtin_cipher_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherBuiltin *ctxt = cipher->opaque; + + return ctxt->setiv(cipher, iv, niv, errp); +} + + +static struct QCryptoCipherDriver qcrypto_cipher_lib_driver = { + .cipher_encrypt = qcrypto_builtin_cipher_encrypt, + .cipher_decrypt = qcrypto_builtin_cipher_decrypt, + .cipher_setiv = qcrypto_builtin_cipher_setiv, + .cipher_free = qcrypto_builtin_cipher_ctx_free, +}; |