/* * 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 . * */ #include "crypto/aes.h" #include "crypto/desrfb.h" #include "crypto/xts.h" typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext; struct QCryptoCipherBuiltinAESContext { AES_KEY enc; AES_KEY dec; }; typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES; struct QCryptoCipherBuiltinAES { QCryptoCipher base; QCryptoCipherBuiltinAESContext key; QCryptoCipherBuiltinAESContext key_tweak; uint8_t iv[AES_BLOCK_SIZE]; }; static inline bool qcrypto_length_check(size_t len, size_t blocksize, Error **errp) { if (unlikely(len & (blocksize - 1))) { error_setg(errp, "Length %zu must be a multiple of block size %zu", len, blocksize); return false; } return true; } static void qcrypto_cipher_ctx_free(QCryptoCipher *cipher) { g_free(cipher); } static int qcrypto_cipher_no_setiv(QCryptoCipher *cipher, const uint8_t *iv, size_t niv, Error **errp) { error_setg(errp, "Setting IV is not supported"); return -1; } static void do_aes_encrypt_ecb(const void *vctx, size_t len, uint8_t *out, const uint8_t *in) { const QCryptoCipherBuiltinAESContext *ctx = vctx; /* We have already verified that len % AES_BLOCK_SIZE == 0. */ while (len) { AES_encrypt(in, out, &ctx->enc); in += AES_BLOCK_SIZE; out += AES_BLOCK_SIZE; len -= AES_BLOCK_SIZE; } } static void do_aes_decrypt_ecb(const void *vctx, size_t len, uint8_t *out, const uint8_t *in) { const QCryptoCipherBuiltinAESContext *ctx = vctx; /* We have already verified that len % AES_BLOCK_SIZE == 0. */ while (len) { AES_decrypt(in, out, &ctx->dec); in += AES_BLOCK_SIZE; out += AES_BLOCK_SIZE; len -= AES_BLOCK_SIZE; } } static void do_aes_encrypt_cbc(const AES_KEY *key, size_t len, uint8_t *out, const uint8_t *in, uint8_t *ivec) { uint8_t tmp[AES_BLOCK_SIZE]; size_t n; /* We have already verified that len % AES_BLOCK_SIZE == 0. */ while (len) { for (n = 0; n < AES_BLOCK_SIZE; ++n) { tmp[n] = in[n] ^ ivec[n]; } AES_encrypt(tmp, out, key); memcpy(ivec, out, AES_BLOCK_SIZE); len -= AES_BLOCK_SIZE; in += AES_BLOCK_SIZE; out += AES_BLOCK_SIZE; } } static void do_aes_decrypt_cbc(const AES_KEY *key, size_t len, uint8_t *out, const uint8_t *in, uint8_t *ivec) { uint8_t tmp[AES_BLOCK_SIZE]; size_t n; /* We have already verified that len % AES_BLOCK_SIZE == 0. */ while (len) { memcpy(tmp, in, AES_BLOCK_SIZE); AES_decrypt(in, out, key); for (n = 0; n < AES_BLOCK_SIZE; ++n) { out[n] ^= ivec[n]; } memcpy(ivec, tmp, AES_BLOCK_SIZE); len -= AES_BLOCK_SIZE; in += AES_BLOCK_SIZE; out += AES_BLOCK_SIZE; } } static int qcrypto_cipher_aes_encrypt_ecb(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { QCryptoCipherBuiltinAES *ctx = container_of(cipher, QCryptoCipherBuiltinAES, base); if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { return -1; } do_aes_encrypt_ecb(&ctx->key, len, out, in); return 0; } static int qcrypto_cipher_aes_decrypt_ecb(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { QCryptoCipherBuiltinAES *ctx = container_of(cipher, QCryptoCipherBuiltinAES, base); if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { return -1; } do_aes_decrypt_ecb(&ctx->key, len, out, in); return 0; } static int qcrypto_cipher_aes_encrypt_cbc(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { QCryptoCipherBuiltinAES *ctx = container_of(cipher, QCryptoCipherBuiltinAES, base); if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { return -1; } do_aes_encrypt_cbc(&ctx->key.enc, len, out, in, ctx->iv); return 0; } static int qcrypto_cipher_aes_decrypt_cbc(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { QCryptoCipherBuiltinAES *ctx = container_of(cipher, QCryptoCipherBuiltinAES, base); if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { return -1; } do_aes_decrypt_cbc(&ctx->key.dec, len, out, in, ctx->iv); return 0; } static int qcrypto_cipher_aes_encrypt_xts(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { QCryptoCipherBuiltinAES *ctx = container_of(cipher, QCryptoCipherBuiltinAES, base); if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { return -1; } xts_encrypt(&ctx->key, &ctx->key_tweak, do_aes_encrypt_ecb, do_aes_decrypt_ecb, ctx->iv, len, out, in); return 0; } static int qcrypto_cipher_aes_decrypt_xts(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { QCryptoCipherBuiltinAES *ctx = container_of(cipher, QCryptoCipherBuiltinAES, base); if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { return -1; } xts_decrypt(&ctx->key, &ctx->key_tweak, do_aes_encrypt_ecb, do_aes_decrypt_ecb, ctx->iv, len, out, in); return 0; } static int qcrypto_cipher_aes_setiv(QCryptoCipher *cipher, const uint8_t *iv, size_t niv, Error **errp) { QCryptoCipherBuiltinAES *ctx = container_of(cipher, QCryptoCipherBuiltinAES, base); if (niv != AES_BLOCK_SIZE) { error_setg(errp, "IV must be %d bytes not %zu", AES_BLOCK_SIZE, niv); return -1; } memcpy(ctx->iv, iv, AES_BLOCK_SIZE); return 0; } static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_ecb = { .cipher_encrypt = qcrypto_cipher_aes_encrypt_ecb, .cipher_decrypt = qcrypto_cipher_aes_decrypt_ecb, .cipher_setiv = qcrypto_cipher_no_setiv, .cipher_free = qcrypto_cipher_ctx_free, }; static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_cbc = { .cipher_encrypt = qcrypto_cipher_aes_encrypt_cbc, .cipher_decrypt = qcrypto_cipher_aes_decrypt_cbc, .cipher_setiv = qcrypto_cipher_aes_setiv, .cipher_free = qcrypto_cipher_ctx_free, }; static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_xts = { .cipher_encrypt = qcrypto_cipher_aes_encrypt_xts, .cipher_decrypt = qcrypto_cipher_aes_decrypt_xts, .cipher_setiv = qcrypto_cipher_aes_setiv, .cipher_free = qcrypto_cipher_ctx_free, }; typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB; struct QCryptoCipherBuiltinDESRFB { QCryptoCipher base; /* C.f. alg_key_len[QCRYPTO_CIPHER_ALG_DES_RFB] */ uint8_t key[8]; }; static int qcrypto_cipher_encrypt_des_rfb(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { QCryptoCipherBuiltinDESRFB *ctx = container_of(cipher, QCryptoCipherBuiltinDESRFB, base); size_t i; if (!qcrypto_length_check(len, 8, errp)) { return -1; } deskey(ctx->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) { QCryptoCipherBuiltinDESRFB *ctx = container_of(cipher, QCryptoCipherBuiltinDESRFB, base); size_t i; if (!qcrypto_length_check(len, 8, errp)) { return -1; } deskey(ctx->key, DE1); for (i = 0; i < len; i += 8) { des((void *)in + i, out + i); } return 0; } static const struct QCryptoCipherDriver qcrypto_cipher_des_rfb_driver = { .cipher_encrypt = qcrypto_cipher_encrypt_des_rfb, .cipher_decrypt = qcrypto_cipher_decrypt_des_rfb, .cipher_setiv = qcrypto_cipher_no_setiv, .cipher_free = qcrypto_cipher_ctx_free, }; bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, QCryptoCipherMode mode) { switch (alg) { case QCRYPTO_CIPHER_ALG_DES_RFB: return mode == QCRYPTO_CIPHER_MODE_ECB; case QCRYPTO_CIPHER_ALG_AES_128: case QCRYPTO_CIPHER_ALG_AES_192: case QCRYPTO_CIPHER_ALG_AES_256: switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: case QCRYPTO_CIPHER_MODE_CBC: case QCRYPTO_CIPHER_MODE_XTS: return true; default: return false; } break; default: return false; } } static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, Error **errp) { if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { return NULL; } switch (alg) { case QCRYPTO_CIPHER_ALG_DES_RFB: if (mode == QCRYPTO_CIPHER_MODE_ECB) { QCryptoCipherBuiltinDESRFB *ctx; ctx = g_new0(QCryptoCipherBuiltinDESRFB, 1); ctx->base.driver = &qcrypto_cipher_des_rfb_driver; memcpy(ctx->key, key, sizeof(ctx->key)); return &ctx->base; } goto bad_mode; case QCRYPTO_CIPHER_ALG_AES_128: case QCRYPTO_CIPHER_ALG_AES_192: case QCRYPTO_CIPHER_ALG_AES_256: { QCryptoCipherBuiltinAES *ctx; const QCryptoCipherDriver *drv; switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: drv = &qcrypto_cipher_aes_driver_ecb; break; case QCRYPTO_CIPHER_MODE_CBC: drv = &qcrypto_cipher_aes_driver_cbc; break; case QCRYPTO_CIPHER_MODE_XTS: drv = &qcrypto_cipher_aes_driver_xts; break; default: goto bad_mode; } ctx = g_new0(QCryptoCipherBuiltinAES, 1); ctx->base.driver = drv; if (mode == QCRYPTO_CIPHER_MODE_XTS) { nkey /= 2; if (AES_set_encrypt_key(key + nkey, nkey * 8, &ctx->key_tweak.enc)) { error_setg(errp, "Failed to set encryption key"); goto error; } if (AES_set_decrypt_key(key + nkey, nkey * 8, &ctx->key_tweak.dec)) { error_setg(errp, "Failed to set decryption key"); goto error; } } if (AES_set_encrypt_key(key, nkey * 8, &ctx->key.enc)) { error_setg(errp, "Failed to set encryption key"); goto error; } if (AES_set_decrypt_key(key, nkey * 8, &ctx->key.dec)) { error_setg(errp, "Failed to set decryption key"); goto error; } return &ctx->base; error: g_free(ctx); return NULL; } default: error_setg(errp, "Unsupported cipher algorithm %s", QCryptoCipherAlgorithm_str(alg)); return NULL; } bad_mode: error_setg(errp, "Unsupported cipher mode %s", QCryptoCipherMode_str(mode)); return NULL; }