| /* |
| * 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(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(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((AES_KEY *)&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((AES_KEY *)&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) { |
| 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) { |
| 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, |
| }; |