| /* |
| * 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[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 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.decrypt_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.decrypt_key); |
| memcpy(outptr, tmp2, len); |
| len = 0; |
| } |
| } |
| } else { |
| AES_cbc_encrypt(in, out, len, |
| &ctxt->state.aes.decrypt_key, |
| ctxt->state.aes.iv, 0); |
| } |
| |
| 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 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->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; |
| |
| 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->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; |
| |
| 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; |
| |
| if (!cipher) { |
| return; |
| } |
| |
| ctxt = cipher->opaque; |
| 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; |
| |
| 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); |
| } |
| |
| |
| int qcrypto_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); |
| } |
| |
| |
| 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); |
| } |