cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

cipher-gnutls.c.inc (10165B)


      1/*
      2 * QEMU Crypto cipher gnutls algorithms
      3 *
      4 * Copyright (c) 2021 Red Hat, Inc.
      5 *
      6 * This library is free software; you can redistribute it and/or
      7 * modify it under the terms of the GNU Lesser General Public
      8 * License as published by the Free Software Foundation; either
      9 * version 2.1 of the License, or (at your option) any later version.
     10 *
     11 * This library is distributed in the hope that it will be useful,
     12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14 * Lesser General Public License for more details.
     15 *
     16 * You should have received a copy of the GNU Lesser General Public
     17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
     18 *
     19 */
     20
     21#include "qemu/osdep.h"
     22#include "cipherpriv.h"
     23
     24#include <gnutls/crypto.h>
     25
     26#if GNUTLS_VERSION_NUMBER >= 0x030608
     27#define QEMU_GNUTLS_XTS
     28#endif
     29
     30bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
     31                             QCryptoCipherMode mode)
     32{
     33
     34    switch (mode) {
     35    case QCRYPTO_CIPHER_MODE_ECB:
     36    case QCRYPTO_CIPHER_MODE_CBC:
     37        switch (alg) {
     38        case QCRYPTO_CIPHER_ALG_AES_128:
     39        case QCRYPTO_CIPHER_ALG_AES_192:
     40        case QCRYPTO_CIPHER_ALG_AES_256:
     41        case QCRYPTO_CIPHER_ALG_DES:
     42        case QCRYPTO_CIPHER_ALG_3DES:
     43            return true;
     44        default:
     45            return false;
     46        }
     47#ifdef QEMU_GNUTLS_XTS
     48    case QCRYPTO_CIPHER_MODE_XTS:
     49        switch (alg) {
     50        case QCRYPTO_CIPHER_ALG_AES_128:
     51        case QCRYPTO_CIPHER_ALG_AES_256:
     52            return true;
     53        default:
     54            return false;
     55        }
     56#endif
     57    default:
     58        return false;
     59    }
     60}
     61
     62typedef struct QCryptoCipherGnutls QCryptoCipherGnutls;
     63struct QCryptoCipherGnutls {
     64    QCryptoCipher base;
     65    gnutls_cipher_hd_t handle; /* XTS & CBC mode */
     66    gnutls_cipher_algorithm_t galg; /* ECB mode */
     67    guint8 *key; /* ECB mode */
     68    size_t nkey; /* ECB mode */
     69    size_t blocksize;
     70};
     71
     72
     73static void
     74qcrypto_gnutls_cipher_free(QCryptoCipher *cipher)
     75{
     76    QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
     77
     78    g_free(ctx->key);
     79    if (ctx->handle) {
     80        gnutls_cipher_deinit(ctx->handle);
     81    }
     82    g_free(ctx);
     83}
     84
     85
     86static int
     87qcrypto_gnutls_cipher_encrypt(QCryptoCipher *cipher,
     88                              const void *in,
     89                              void *out,
     90                              size_t len,
     91                              Error **errp)
     92{
     93    QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
     94    int err;
     95
     96    if (len % ctx->blocksize) {
     97        error_setg(errp, "Length %zu must be a multiple of block size %zu",
     98                   len, ctx->blocksize);
     99        return -1;
    100    }
    101
    102    if (ctx->handle) { /* CBC / XTS mode */
    103        err = gnutls_cipher_encrypt2(ctx->handle,
    104                                     in, len,
    105                                     out, len);
    106        if (err != 0) {
    107            error_setg(errp, "Cannot encrypt data: %s",
    108                       gnutls_strerror(err));
    109            return -1;
    110        }
    111    } else { /* ECB mode very inefficiently faked with CBC */
    112        g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
    113        while (len) {
    114            gnutls_cipher_hd_t handle;
    115            gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
    116            int err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
    117            if (err != 0) {
    118                error_setg(errp, "Cannot initialize cipher: %s",
    119                           gnutls_strerror(err));
    120                return -1;
    121            }
    122
    123            gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
    124
    125            err = gnutls_cipher_encrypt2(handle,
    126                                         in, ctx->blocksize,
    127                                         out, ctx->blocksize);
    128            if (err != 0) {
    129                gnutls_cipher_deinit(handle);
    130                error_setg(errp, "Cannot encrypt data: %s",
    131                           gnutls_strerror(err));
    132                return -1;
    133            }
    134            gnutls_cipher_deinit(handle);
    135
    136            len -= ctx->blocksize;
    137            in += ctx->blocksize;
    138            out += ctx->blocksize;
    139        }
    140    }
    141
    142    return 0;
    143}
    144
    145
    146static int
    147qcrypto_gnutls_cipher_decrypt(QCryptoCipher *cipher,
    148                              const void *in,
    149                              void *out,
    150                              size_t len,
    151                              Error **errp)
    152{
    153    QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
    154    int err;
    155
    156    if (len % ctx->blocksize) {
    157        error_setg(errp, "Length %zu must be a multiple of block size %zu",
    158                   len, ctx->blocksize);
    159        return -1;
    160    }
    161
    162    if (ctx->handle) { /* CBC / XTS mode */
    163        err = gnutls_cipher_decrypt2(ctx->handle,
    164                                     in, len,
    165                                     out, len);
    166
    167        if (err != 0) {
    168            error_setg(errp, "Cannot decrypt data: %s",
    169                       gnutls_strerror(err));
    170            return -1;
    171        }
    172    } else { /* ECB mode very inefficiently faked with CBC */
    173        g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
    174        while (len) {
    175            gnutls_cipher_hd_t handle;
    176            gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
    177            int err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
    178            if (err != 0) {
    179                error_setg(errp, "Cannot initialize cipher: %s",
    180                           gnutls_strerror(err));
    181                return -1;
    182            }
    183
    184            gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
    185
    186            err = gnutls_cipher_decrypt2(handle,
    187                                         in, ctx->blocksize,
    188                                         out, ctx->blocksize);
    189            if (err != 0) {
    190                gnutls_cipher_deinit(handle);
    191                error_setg(errp, "Cannot encrypt data: %s",
    192                           gnutls_strerror(err));
    193                return -1;
    194            }
    195            gnutls_cipher_deinit(handle);
    196
    197            len -= ctx->blocksize;
    198            in += ctx->blocksize;
    199            out += ctx->blocksize;
    200        }
    201    }
    202
    203    return 0;
    204}
    205
    206static int
    207qcrypto_gnutls_cipher_setiv(QCryptoCipher *cipher,
    208                            const uint8_t *iv, size_t niv,
    209                            Error **errp)
    210{
    211    QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
    212
    213    if (niv != ctx->blocksize) {
    214        error_setg(errp, "Expected IV size %zu not %zu",
    215                   ctx->blocksize, niv);
    216        return -1;
    217    }
    218
    219    gnutls_cipher_set_iv(ctx->handle, (unsigned char *)iv, niv);
    220
    221    return 0;
    222}
    223
    224
    225static struct QCryptoCipherDriver gnutls_driver = {
    226    .cipher_encrypt = qcrypto_gnutls_cipher_encrypt,
    227    .cipher_decrypt = qcrypto_gnutls_cipher_decrypt,
    228    .cipher_setiv = qcrypto_gnutls_cipher_setiv,
    229    .cipher_free = qcrypto_gnutls_cipher_free,
    230};
    231
    232static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
    233                                             QCryptoCipherMode mode,
    234                                             const uint8_t *key,
    235                                             size_t nkey,
    236                                             Error **errp)
    237{
    238    QCryptoCipherGnutls *ctx;
    239    gnutls_datum_t gkey = { (unsigned char *)key, nkey };
    240    gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN;
    241    int err;
    242
    243    switch (mode) {
    244#ifdef QEMU_GNUTLS_XTS
    245    case QCRYPTO_CIPHER_MODE_XTS:
    246        switch (alg) {
    247        case QCRYPTO_CIPHER_ALG_AES_128:
    248            galg = GNUTLS_CIPHER_AES_128_XTS;
    249            break;
    250        case QCRYPTO_CIPHER_ALG_AES_256:
    251            galg = GNUTLS_CIPHER_AES_256_XTS;
    252            break;
    253        default:
    254            break;
    255        }
    256        break;
    257#endif
    258
    259    case QCRYPTO_CIPHER_MODE_ECB:
    260    case QCRYPTO_CIPHER_MODE_CBC:
    261        switch (alg) {
    262        case QCRYPTO_CIPHER_ALG_AES_128:
    263            galg = GNUTLS_CIPHER_AES_128_CBC;
    264            break;
    265        case QCRYPTO_CIPHER_ALG_AES_192:
    266            galg = GNUTLS_CIPHER_AES_192_CBC;
    267            break;
    268        case QCRYPTO_CIPHER_ALG_AES_256:
    269            galg = GNUTLS_CIPHER_AES_256_CBC;
    270            break;
    271        case QCRYPTO_CIPHER_ALG_DES:
    272            galg = GNUTLS_CIPHER_DES_CBC;
    273            break;
    274        case QCRYPTO_CIPHER_ALG_3DES:
    275            galg = GNUTLS_CIPHER_3DES_CBC;
    276            break;
    277        default:
    278            break;
    279        }
    280        break;
    281    default:
    282        break;
    283    }
    284
    285    if (galg == GNUTLS_CIPHER_UNKNOWN) {
    286        error_setg(errp, "Unsupported cipher algorithm %s with %s mode",
    287                   QCryptoCipherAlgorithm_str(alg),
    288                   QCryptoCipherMode_str(mode));
    289        return NULL;
    290    }
    291
    292    if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
    293        return NULL;
    294    }
    295
    296    ctx = g_new0(QCryptoCipherGnutls, 1);
    297    ctx->base.driver = &gnutls_driver;
    298
    299    if (mode == QCRYPTO_CIPHER_MODE_ECB) {
    300        ctx->key = g_new0(guint8, nkey);
    301        memcpy(ctx->key, key, nkey);
    302        ctx->nkey = nkey;
    303        ctx->galg = galg;
    304    } else {
    305        err = gnutls_cipher_init(&ctx->handle, galg, &gkey, NULL);
    306        if (err != 0) {
    307            error_setg(errp, "Cannot initialize cipher: %s",
    308                       gnutls_strerror(err));
    309            goto error;
    310        }
    311    }
    312
    313    if (alg == QCRYPTO_CIPHER_ALG_DES ||
    314        alg == QCRYPTO_CIPHER_ALG_3DES)
    315        ctx->blocksize = 8;
    316    else
    317        ctx->blocksize = 16;
    318
    319    /*
    320     * Our API contract for requires iv to be optional
    321     * but nettle gets unhappy when called by gnutls
    322     * in this case, so we just force set a default
    323     * all-zeros IV, to match behaviour of other backends.
    324     */
    325    if (mode != QCRYPTO_CIPHER_MODE_ECB) {
    326        g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
    327        gnutls_cipher_set_iv(ctx->handle, iv, ctx->blocksize);
    328    }
    329
    330    return &ctx->base;
    331
    332 error:
    333    qcrypto_gnutls_cipher_free(&ctx->base);
    334    return NULL;
    335}