| /* | 
 |  * 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 "crypto/aes.h" | 
 |  | 
 | typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext; | 
 | struct QCryptoCipherBuiltinAESContext { | 
 |     AES_KEY enc; | 
 |     AES_KEY dec; | 
 | }; | 
 |  | 
 | typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES; | 
 | struct QCryptoCipherBuiltinAES { | 
 |     QCryptoCipher base; | 
 |     QCryptoCipherBuiltinAESContext key; | 
 |     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_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, | 
 | }; | 
 |  | 
 | bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, | 
 |                              QCryptoCipherMode mode) | 
 | { | 
 |     switch (alg) { | 
 |     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: | 
 |             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_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; | 
 |             default: | 
 |                 goto bad_mode; | 
 |             } | 
 |  | 
 |             ctx = g_new0(QCryptoCipherBuiltinAES, 1); | 
 |             ctx->base.driver = drv; | 
 |  | 
 |             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; | 
 | } |