qemu/crypto/cipher-gcrypt.c
<<
>>
Prefs
   1/*
   2 * QEMU Crypto cipher libgcrypt algorithms
   3 *
   4 * Copyright (c) 2015 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 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 "crypto/xts.h"
  23
  24#include <gcrypt.h>
  25
  26
  27bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
  28{
  29    switch (alg) {
  30    case QCRYPTO_CIPHER_ALG_DES_RFB:
  31    case QCRYPTO_CIPHER_ALG_AES_128:
  32    case QCRYPTO_CIPHER_ALG_AES_192:
  33    case QCRYPTO_CIPHER_ALG_AES_256:
  34    case QCRYPTO_CIPHER_ALG_CAST5_128:
  35    case QCRYPTO_CIPHER_ALG_SERPENT_128:
  36    case QCRYPTO_CIPHER_ALG_SERPENT_192:
  37    case QCRYPTO_CIPHER_ALG_SERPENT_256:
  38    case QCRYPTO_CIPHER_ALG_TWOFISH_128:
  39    case QCRYPTO_CIPHER_ALG_TWOFISH_256:
  40        return true;
  41    default:
  42        return false;
  43    }
  44}
  45
  46typedef struct QCryptoCipherGcrypt QCryptoCipherGcrypt;
  47struct QCryptoCipherGcrypt {
  48    gcry_cipher_hd_t handle;
  49    gcry_cipher_hd_t tweakhandle;
  50    size_t blocksize;
  51    uint8_t *iv;
  52};
  53
  54QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
  55                                  QCryptoCipherMode mode,
  56                                  const uint8_t *key, size_t nkey,
  57                                  Error **errp)
  58{
  59    QCryptoCipher *cipher;
  60    QCryptoCipherGcrypt *ctx;
  61    gcry_error_t err;
  62    int gcryalg, gcrymode;
  63
  64    switch (mode) {
  65    case QCRYPTO_CIPHER_MODE_ECB:
  66    case QCRYPTO_CIPHER_MODE_XTS:
  67        gcrymode = GCRY_CIPHER_MODE_ECB;
  68        break;
  69    case QCRYPTO_CIPHER_MODE_CBC:
  70        gcrymode = GCRY_CIPHER_MODE_CBC;
  71        break;
  72    default:
  73        error_setg(errp, "Unsupported cipher mode %d", mode);
  74        return NULL;
  75    }
  76
  77    if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
  78        return NULL;
  79    }
  80
  81    switch (alg) {
  82    case QCRYPTO_CIPHER_ALG_DES_RFB:
  83        gcryalg = GCRY_CIPHER_DES;
  84        break;
  85
  86    case QCRYPTO_CIPHER_ALG_AES_128:
  87        gcryalg = GCRY_CIPHER_AES128;
  88        break;
  89
  90    case QCRYPTO_CIPHER_ALG_AES_192:
  91        gcryalg = GCRY_CIPHER_AES192;
  92        break;
  93
  94    case QCRYPTO_CIPHER_ALG_AES_256:
  95        gcryalg = GCRY_CIPHER_AES256;
  96        break;
  97
  98    case QCRYPTO_CIPHER_ALG_CAST5_128:
  99        gcryalg = GCRY_CIPHER_CAST5;
 100        break;
 101
 102    case QCRYPTO_CIPHER_ALG_SERPENT_128:
 103        gcryalg = GCRY_CIPHER_SERPENT128;
 104        break;
 105
 106    case QCRYPTO_CIPHER_ALG_SERPENT_192:
 107        gcryalg = GCRY_CIPHER_SERPENT192;
 108        break;
 109
 110    case QCRYPTO_CIPHER_ALG_SERPENT_256:
 111        gcryalg = GCRY_CIPHER_SERPENT256;
 112        break;
 113
 114    case QCRYPTO_CIPHER_ALG_TWOFISH_128:
 115        gcryalg = GCRY_CIPHER_TWOFISH128;
 116        break;
 117
 118    case QCRYPTO_CIPHER_ALG_TWOFISH_256:
 119        gcryalg = GCRY_CIPHER_TWOFISH;
 120        break;
 121
 122    default:
 123        error_setg(errp, "Unsupported cipher algorithm %d", alg);
 124        return NULL;
 125    }
 126
 127    cipher = g_new0(QCryptoCipher, 1);
 128    cipher->alg = alg;
 129    cipher->mode = mode;
 130
 131    ctx = g_new0(QCryptoCipherGcrypt, 1);
 132
 133    err = gcry_cipher_open(&ctx->handle, gcryalg, gcrymode, 0);
 134    if (err != 0) {
 135        error_setg(errp, "Cannot initialize cipher: %s",
 136                   gcry_strerror(err));
 137        goto error;
 138    }
 139    if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
 140        err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0);
 141        if (err != 0) {
 142            error_setg(errp, "Cannot initialize cipher: %s",
 143                       gcry_strerror(err));
 144            goto error;
 145        }
 146    }
 147
 148    if (cipher->alg == QCRYPTO_CIPHER_ALG_DES_RFB) {
 149        /* We're using standard DES cipher from gcrypt, so we need
 150         * to munge the key so that the results are the same as the
 151         * bizarre RFB variant of DES :-)
 152         */
 153        uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
 154        err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey);
 155        g_free(rfbkey);
 156        ctx->blocksize = 8;
 157    } else {
 158        if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
 159            nkey /= 2;
 160            err = gcry_cipher_setkey(ctx->handle, key, nkey);
 161            if (err != 0) {
 162                error_setg(errp, "Cannot set key: %s",
 163                           gcry_strerror(err));
 164                goto error;
 165            }
 166            err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey);
 167        } else {
 168            err = gcry_cipher_setkey(ctx->handle, key, nkey);
 169        }
 170        if (err != 0) {
 171            error_setg(errp, "Cannot set key: %s",
 172                       gcry_strerror(err));
 173            goto error;
 174        }
 175        switch (cipher->alg) {
 176        case QCRYPTO_CIPHER_ALG_AES_128:
 177        case QCRYPTO_CIPHER_ALG_AES_192:
 178        case QCRYPTO_CIPHER_ALG_AES_256:
 179        case QCRYPTO_CIPHER_ALG_SERPENT_128:
 180        case QCRYPTO_CIPHER_ALG_SERPENT_192:
 181        case QCRYPTO_CIPHER_ALG_SERPENT_256:
 182        case QCRYPTO_CIPHER_ALG_TWOFISH_128:
 183        case QCRYPTO_CIPHER_ALG_TWOFISH_256:
 184            ctx->blocksize = 16;
 185            break;
 186        case QCRYPTO_CIPHER_ALG_CAST5_128:
 187            ctx->blocksize = 8;
 188            break;
 189        default:
 190            g_assert_not_reached();
 191        }
 192    }
 193
 194    if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
 195        ctx->iv = g_new0(uint8_t, ctx->blocksize);
 196    }
 197
 198    cipher->opaque = ctx;
 199    return cipher;
 200
 201 error:
 202    gcry_cipher_close(ctx->handle);
 203    if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
 204        gcry_cipher_close(ctx->tweakhandle);
 205    }
 206    g_free(ctx);
 207    g_free(cipher);
 208    return NULL;
 209}
 210
 211
 212void qcrypto_cipher_free(QCryptoCipher *cipher)
 213{
 214    QCryptoCipherGcrypt *ctx;
 215    if (!cipher) {
 216        return;
 217    }
 218    ctx = cipher->opaque;
 219    gcry_cipher_close(ctx->handle);
 220    if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
 221        gcry_cipher_close(ctx->tweakhandle);
 222    }
 223    g_free(ctx->iv);
 224    g_free(ctx);
 225    g_free(cipher);
 226}
 227
 228
 229static void qcrypto_gcrypt_xts_encrypt(const void *ctx,
 230                                       size_t length,
 231                                       uint8_t *dst,
 232                                       const uint8_t *src)
 233{
 234    gcry_error_t err;
 235    err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
 236    g_assert(err == 0);
 237}
 238
 239static void qcrypto_gcrypt_xts_decrypt(const void *ctx,
 240                                       size_t length,
 241                                       uint8_t *dst,
 242                                       const uint8_t *src)
 243{
 244    gcry_error_t err;
 245    err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
 246    g_assert(err == 0);
 247}
 248
 249int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
 250                           const void *in,
 251                           void *out,
 252                           size_t len,
 253                           Error **errp)
 254{
 255    QCryptoCipherGcrypt *ctx = cipher->opaque;
 256    gcry_error_t err;
 257
 258    if (len % ctx->blocksize) {
 259        error_setg(errp, "Length %zu must be a multiple of block size %zu",
 260                   len, ctx->blocksize);
 261        return -1;
 262    }
 263
 264    if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
 265        xts_encrypt(ctx->handle, ctx->tweakhandle,
 266                    qcrypto_gcrypt_xts_encrypt,
 267                    qcrypto_gcrypt_xts_decrypt,
 268                    ctx->iv, len, out, in);
 269    } else {
 270        err = gcry_cipher_encrypt(ctx->handle,
 271                                  out, len,
 272                                  in, len);
 273        if (err != 0) {
 274            error_setg(errp, "Cannot encrypt data: %s",
 275                       gcry_strerror(err));
 276            return -1;
 277        }
 278    }
 279
 280    return 0;
 281}
 282
 283
 284int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
 285                           const void *in,
 286                           void *out,
 287                           size_t len,
 288                           Error **errp)
 289{
 290    QCryptoCipherGcrypt *ctx = cipher->opaque;
 291    gcry_error_t err;
 292
 293    if (len % ctx->blocksize) {
 294        error_setg(errp, "Length %zu must be a multiple of block size %zu",
 295                   len, ctx->blocksize);
 296        return -1;
 297    }
 298
 299    if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
 300        xts_decrypt(ctx->handle, ctx->tweakhandle,
 301                    qcrypto_gcrypt_xts_encrypt,
 302                    qcrypto_gcrypt_xts_decrypt,
 303                    ctx->iv, len, out, in);
 304    } else {
 305        err = gcry_cipher_decrypt(ctx->handle,
 306                                  out, len,
 307                                  in, len);
 308        if (err != 0) {
 309            error_setg(errp, "Cannot decrypt data: %s",
 310                       gcry_strerror(err));
 311            return -1;
 312        }
 313    }
 314
 315    return 0;
 316}
 317
 318int qcrypto_cipher_setiv(QCryptoCipher *cipher,
 319                         const uint8_t *iv, size_t niv,
 320                         Error **errp)
 321{
 322    QCryptoCipherGcrypt *ctx = cipher->opaque;
 323    gcry_error_t err;
 324
 325    if (niv != ctx->blocksize) {
 326        error_setg(errp, "Expected IV size %zu not %zu",
 327                   ctx->blocksize, niv);
 328        return -1;
 329    }
 330
 331    if (ctx->iv) {
 332        memcpy(ctx->iv, iv, niv);
 333    } else {
 334        gcry_cipher_reset(ctx->handle);
 335        err = gcry_cipher_setiv(ctx->handle, iv, niv);
 336        if (err != 0) {
 337            error_setg(errp, "Cannot set IV: %s",
 338                   gcry_strerror(err));
 339            return -1;
 340        }
 341    }
 342
 343    return 0;
 344}
 345