xref: /openbmc/qemu/crypto/cipher-gcrypt.c.inc (revision 3eedf5cc9d45f94e2fd229f0a7aaca556a4ac734)
16d92bdf4SRichard Henderson/*
26d92bdf4SRichard Henderson * QEMU Crypto cipher libgcrypt algorithms
36d92bdf4SRichard Henderson *
46d92bdf4SRichard Henderson * Copyright (c) 2015 Red Hat, Inc.
56d92bdf4SRichard Henderson *
66d92bdf4SRichard Henderson * This library is free software; you can redistribute it and/or
76d92bdf4SRichard Henderson * modify it under the terms of the GNU Lesser General Public
86d92bdf4SRichard Henderson * License as published by the Free Software Foundation; either
96d92bdf4SRichard Henderson * version 2.1 of the License, or (at your option) any later version.
106d92bdf4SRichard Henderson *
116d92bdf4SRichard Henderson * This library is distributed in the hope that it will be useful,
126d92bdf4SRichard Henderson * but WITHOUT ANY WARRANTY; without even the implied warranty of
136d92bdf4SRichard Henderson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
146d92bdf4SRichard Henderson * Lesser General Public License for more details.
156d92bdf4SRichard Henderson *
166d92bdf4SRichard Henderson * You should have received a copy of the GNU Lesser General Public
176d92bdf4SRichard Henderson * License along with this library; if not, see <http://www.gnu.org/licenses/>.
186d92bdf4SRichard Henderson *
196d92bdf4SRichard Henderson */
206d92bdf4SRichard Henderson
216d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
226d92bdf4SRichard Henderson#include "crypto/xts.h"
236d92bdf4SRichard Henderson#endif
246d92bdf4SRichard Henderson
256d92bdf4SRichard Henderson#include <gcrypt.h>
266d92bdf4SRichard Henderson
276d92bdf4SRichard Henderson
286d92bdf4SRichard Hendersonbool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
296d92bdf4SRichard Henderson                             QCryptoCipherMode mode)
306d92bdf4SRichard Henderson{
316d92bdf4SRichard Henderson    switch (alg) {
326d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_DES_RFB:
336d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_3DES:
346d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_AES_128:
356d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_AES_192:
366d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_AES_256:
376d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_CAST5_128:
386d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_SERPENT_128:
396d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_SERPENT_192:
406d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_SERPENT_256:
416d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_TWOFISH_128:
426d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_TWOFISH_256:
436d92bdf4SRichard Henderson        break;
446d92bdf4SRichard Henderson    default:
456d92bdf4SRichard Henderson        return false;
466d92bdf4SRichard Henderson    }
476d92bdf4SRichard Henderson
486d92bdf4SRichard Henderson    switch (mode) {
496d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_MODE_ECB:
506d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_MODE_CBC:
516d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_MODE_XTS:
526d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_MODE_CTR:
536d92bdf4SRichard Henderson        return true;
546d92bdf4SRichard Henderson    default:
556d92bdf4SRichard Henderson        return false;
566d92bdf4SRichard Henderson    }
576d92bdf4SRichard Henderson}
586d92bdf4SRichard Henderson
596d92bdf4SRichard Hendersontypedef struct QCryptoCipherGcrypt QCryptoCipherGcrypt;
606d92bdf4SRichard Hendersonstruct QCryptoCipherGcrypt {
61*3eedf5ccSRichard Henderson    QCryptoCipher base;
626d92bdf4SRichard Henderson    gcry_cipher_hd_t handle;
636d92bdf4SRichard Henderson    size_t blocksize;
646d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
656d92bdf4SRichard Henderson    gcry_cipher_hd_t tweakhandle;
666d92bdf4SRichard Henderson    /* Initialization vector or Counter */
676d92bdf4SRichard Henderson    uint8_t *iv;
686d92bdf4SRichard Henderson#endif
696d92bdf4SRichard Henderson};
706d92bdf4SRichard Henderson
716d92bdf4SRichard Hendersonstatic void
726d92bdf4SRichard Hendersonqcrypto_gcrypt_cipher_free_ctx(QCryptoCipherGcrypt *ctx,
736d92bdf4SRichard Henderson                               QCryptoCipherMode mode)
746d92bdf4SRichard Henderson{
756d92bdf4SRichard Henderson    if (!ctx) {
766d92bdf4SRichard Henderson        return;
776d92bdf4SRichard Henderson    }
786d92bdf4SRichard Henderson
796d92bdf4SRichard Henderson    gcry_cipher_close(ctx->handle);
806d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
816d92bdf4SRichard Henderson    if (mode == QCRYPTO_CIPHER_MODE_XTS) {
826d92bdf4SRichard Henderson        gcry_cipher_close(ctx->tweakhandle);
836d92bdf4SRichard Henderson    }
846d92bdf4SRichard Henderson    g_free(ctx->iv);
856d92bdf4SRichard Henderson#endif
866d92bdf4SRichard Henderson    g_free(ctx);
876d92bdf4SRichard Henderson}
886d92bdf4SRichard Henderson
896d92bdf4SRichard Henderson
90*3eedf5ccSRichard Hendersonstatic QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
916d92bdf4SRichard Henderson                                             QCryptoCipherMode mode,
926d92bdf4SRichard Henderson                                             const uint8_t *key,
936d92bdf4SRichard Henderson                                             size_t nkey,
946d92bdf4SRichard Henderson                                             Error **errp)
956d92bdf4SRichard Henderson{
966d92bdf4SRichard Henderson    QCryptoCipherGcrypt *ctx;
976d92bdf4SRichard Henderson    gcry_error_t err;
986d92bdf4SRichard Henderson    int gcryalg, gcrymode;
996d92bdf4SRichard Henderson
1006d92bdf4SRichard Henderson    switch (mode) {
1016d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_MODE_ECB:
1026d92bdf4SRichard Henderson        gcrymode = GCRY_CIPHER_MODE_ECB;
1036d92bdf4SRichard Henderson        break;
1046d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_MODE_XTS:
1056d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
1066d92bdf4SRichard Henderson        gcrymode = GCRY_CIPHER_MODE_ECB;
1076d92bdf4SRichard Henderson#else
1086d92bdf4SRichard Henderson        gcrymode = GCRY_CIPHER_MODE_XTS;
1096d92bdf4SRichard Henderson#endif
1106d92bdf4SRichard Henderson        break;
1116d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_MODE_CBC:
1126d92bdf4SRichard Henderson        gcrymode = GCRY_CIPHER_MODE_CBC;
1136d92bdf4SRichard Henderson        break;
1146d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_MODE_CTR:
1156d92bdf4SRichard Henderson        gcrymode = GCRY_CIPHER_MODE_CTR;
1166d92bdf4SRichard Henderson        break;
1176d92bdf4SRichard Henderson    default:
1186d92bdf4SRichard Henderson        error_setg(errp, "Unsupported cipher mode %s",
1196d92bdf4SRichard Henderson                   QCryptoCipherMode_str(mode));
1206d92bdf4SRichard Henderson        return NULL;
1216d92bdf4SRichard Henderson    }
1226d92bdf4SRichard Henderson
1236d92bdf4SRichard Henderson    if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
1246d92bdf4SRichard Henderson        return NULL;
1256d92bdf4SRichard Henderson    }
1266d92bdf4SRichard Henderson
1276d92bdf4SRichard Henderson    switch (alg) {
1286d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_DES_RFB:
1296d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_DES;
1306d92bdf4SRichard Henderson        break;
1316d92bdf4SRichard Henderson
1326d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_3DES:
1336d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_3DES;
1346d92bdf4SRichard Henderson        break;
1356d92bdf4SRichard Henderson
1366d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_AES_128:
1376d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_AES128;
1386d92bdf4SRichard Henderson        break;
1396d92bdf4SRichard Henderson
1406d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_AES_192:
1416d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_AES192;
1426d92bdf4SRichard Henderson        break;
1436d92bdf4SRichard Henderson
1446d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_AES_256:
1456d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_AES256;
1466d92bdf4SRichard Henderson        break;
1476d92bdf4SRichard Henderson
1486d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_CAST5_128:
1496d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_CAST5;
1506d92bdf4SRichard Henderson        break;
1516d92bdf4SRichard Henderson
1526d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_SERPENT_128:
1536d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_SERPENT128;
1546d92bdf4SRichard Henderson        break;
1556d92bdf4SRichard Henderson
1566d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_SERPENT_192:
1576d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_SERPENT192;
1586d92bdf4SRichard Henderson        break;
1596d92bdf4SRichard Henderson
1606d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_SERPENT_256:
1616d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_SERPENT256;
1626d92bdf4SRichard Henderson        break;
1636d92bdf4SRichard Henderson
1646d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_TWOFISH_128:
1656d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_TWOFISH128;
1666d92bdf4SRichard Henderson        break;
1676d92bdf4SRichard Henderson
1686d92bdf4SRichard Henderson    case QCRYPTO_CIPHER_ALG_TWOFISH_256:
1696d92bdf4SRichard Henderson        gcryalg = GCRY_CIPHER_TWOFISH;
1706d92bdf4SRichard Henderson        break;
1716d92bdf4SRichard Henderson
1726d92bdf4SRichard Henderson    default:
1736d92bdf4SRichard Henderson        error_setg(errp, "Unsupported cipher algorithm %s",
1746d92bdf4SRichard Henderson                   QCryptoCipherAlgorithm_str(alg));
1756d92bdf4SRichard Henderson        return NULL;
1766d92bdf4SRichard Henderson    }
1776d92bdf4SRichard Henderson
1786d92bdf4SRichard Henderson    ctx = g_new0(QCryptoCipherGcrypt, 1);
1796d92bdf4SRichard Henderson
1806d92bdf4SRichard Henderson    err = gcry_cipher_open(&ctx->handle, gcryalg, gcrymode, 0);
1816d92bdf4SRichard Henderson    if (err != 0) {
1826d92bdf4SRichard Henderson        error_setg(errp, "Cannot initialize cipher: %s",
1836d92bdf4SRichard Henderson                   gcry_strerror(err));
1846d92bdf4SRichard Henderson        goto error;
1856d92bdf4SRichard Henderson    }
1866d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
1876d92bdf4SRichard Henderson    if (mode == QCRYPTO_CIPHER_MODE_XTS) {
1886d92bdf4SRichard Henderson        err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0);
1896d92bdf4SRichard Henderson        if (err != 0) {
1906d92bdf4SRichard Henderson            error_setg(errp, "Cannot initialize cipher: %s",
1916d92bdf4SRichard Henderson                       gcry_strerror(err));
1926d92bdf4SRichard Henderson            goto error;
1936d92bdf4SRichard Henderson        }
1946d92bdf4SRichard Henderson    }
1956d92bdf4SRichard Henderson#endif
1966d92bdf4SRichard Henderson
1976d92bdf4SRichard Henderson    if (alg == QCRYPTO_CIPHER_ALG_DES_RFB) {
1986d92bdf4SRichard Henderson        /* We're using standard DES cipher from gcrypt, so we need
1996d92bdf4SRichard Henderson         * to munge the key so that the results are the same as the
2006d92bdf4SRichard Henderson         * bizarre RFB variant of DES :-)
2016d92bdf4SRichard Henderson         */
2026d92bdf4SRichard Henderson        uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
2036d92bdf4SRichard Henderson        err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey);
2046d92bdf4SRichard Henderson        g_free(rfbkey);
2056d92bdf4SRichard Henderson        ctx->blocksize = 8;
2066d92bdf4SRichard Henderson    } else {
2076d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
2086d92bdf4SRichard Henderson        if (mode == QCRYPTO_CIPHER_MODE_XTS) {
2096d92bdf4SRichard Henderson            nkey /= 2;
2106d92bdf4SRichard Henderson            err = gcry_cipher_setkey(ctx->handle, key, nkey);
2116d92bdf4SRichard Henderson            if (err != 0) {
2126d92bdf4SRichard Henderson                error_setg(errp, "Cannot set key: %s",
2136d92bdf4SRichard Henderson                           gcry_strerror(err));
2146d92bdf4SRichard Henderson                goto error;
2156d92bdf4SRichard Henderson            }
2166d92bdf4SRichard Henderson            err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey);
2176d92bdf4SRichard Henderson        } else {
2186d92bdf4SRichard Henderson#endif
2196d92bdf4SRichard Henderson            err = gcry_cipher_setkey(ctx->handle, key, nkey);
2206d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
2216d92bdf4SRichard Henderson        }
2226d92bdf4SRichard Henderson#endif
2236d92bdf4SRichard Henderson        if (err != 0) {
2246d92bdf4SRichard Henderson            error_setg(errp, "Cannot set key: %s",
2256d92bdf4SRichard Henderson                       gcry_strerror(err));
2266d92bdf4SRichard Henderson            goto error;
2276d92bdf4SRichard Henderson        }
2286d92bdf4SRichard Henderson        switch (alg) {
2296d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_AES_128:
2306d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_AES_192:
2316d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_AES_256:
2326d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_SERPENT_128:
2336d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_SERPENT_192:
2346d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_SERPENT_256:
2356d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_TWOFISH_128:
2366d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_TWOFISH_256:
2376d92bdf4SRichard Henderson            ctx->blocksize = 16;
2386d92bdf4SRichard Henderson            break;
2396d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_3DES:
2406d92bdf4SRichard Henderson        case QCRYPTO_CIPHER_ALG_CAST5_128:
2416d92bdf4SRichard Henderson            ctx->blocksize = 8;
2426d92bdf4SRichard Henderson            break;
2436d92bdf4SRichard Henderson        default:
2446d92bdf4SRichard Henderson            g_assert_not_reached();
2456d92bdf4SRichard Henderson        }
2466d92bdf4SRichard Henderson    }
2476d92bdf4SRichard Henderson    g_assert(is_power_of_2(ctx->blocksize));
2486d92bdf4SRichard Henderson
2496d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
2506d92bdf4SRichard Henderson    if (mode == QCRYPTO_CIPHER_MODE_XTS) {
2516d92bdf4SRichard Henderson        if (ctx->blocksize != XTS_BLOCK_SIZE) {
2526d92bdf4SRichard Henderson            error_setg(errp,
2536d92bdf4SRichard Henderson                       "Cipher block size %zu must equal XTS block size %d",
2546d92bdf4SRichard Henderson                       ctx->blocksize, XTS_BLOCK_SIZE);
2556d92bdf4SRichard Henderson            goto error;
2566d92bdf4SRichard Henderson        }
2576d92bdf4SRichard Henderson        ctx->iv = g_new0(uint8_t, ctx->blocksize);
2586d92bdf4SRichard Henderson    }
2596d92bdf4SRichard Henderson#endif
2606d92bdf4SRichard Henderson
261*3eedf5ccSRichard Henderson    return &ctx->base;
2626d92bdf4SRichard Henderson
2636d92bdf4SRichard Henderson error:
2646d92bdf4SRichard Henderson    qcrypto_gcrypt_cipher_free_ctx(ctx, mode);
2656d92bdf4SRichard Henderson    return NULL;
2666d92bdf4SRichard Henderson}
2676d92bdf4SRichard Henderson
2686d92bdf4SRichard Henderson
2696d92bdf4SRichard Hendersonstatic void
2706d92bdf4SRichard Hendersonqcrypto_gcrypt_cipher_ctx_free(QCryptoCipher *cipher)
2716d92bdf4SRichard Henderson{
272*3eedf5ccSRichard Henderson    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
273*3eedf5ccSRichard Henderson
274*3eedf5ccSRichard Henderson    qcrypto_gcrypt_cipher_free_ctx(ctx, cipher->mode);
2756d92bdf4SRichard Henderson}
2766d92bdf4SRichard Henderson
2776d92bdf4SRichard Henderson
2786d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
2796d92bdf4SRichard Hendersonstatic void qcrypto_gcrypt_xts_encrypt(const void *ctx,
2806d92bdf4SRichard Henderson                                       size_t length,
2816d92bdf4SRichard Henderson                                       uint8_t *dst,
2826d92bdf4SRichard Henderson                                       const uint8_t *src)
2836d92bdf4SRichard Henderson{
2846d92bdf4SRichard Henderson    gcry_error_t err;
2856d92bdf4SRichard Henderson    err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
2866d92bdf4SRichard Henderson    g_assert(err == 0);
2876d92bdf4SRichard Henderson}
2886d92bdf4SRichard Henderson
2896d92bdf4SRichard Hendersonstatic void qcrypto_gcrypt_xts_decrypt(const void *ctx,
2906d92bdf4SRichard Henderson                                       size_t length,
2916d92bdf4SRichard Henderson                                       uint8_t *dst,
2926d92bdf4SRichard Henderson                                       const uint8_t *src)
2936d92bdf4SRichard Henderson{
2946d92bdf4SRichard Henderson    gcry_error_t err;
2956d92bdf4SRichard Henderson    err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
2966d92bdf4SRichard Henderson    g_assert(err == 0);
2976d92bdf4SRichard Henderson}
2986d92bdf4SRichard Henderson#endif
2996d92bdf4SRichard Henderson
3006d92bdf4SRichard Hendersonstatic int
3016d92bdf4SRichard Hendersonqcrypto_gcrypt_cipher_encrypt(QCryptoCipher *cipher,
3026d92bdf4SRichard Henderson                              const void *in,
3036d92bdf4SRichard Henderson                              void *out,
3046d92bdf4SRichard Henderson                              size_t len,
3056d92bdf4SRichard Henderson                              Error **errp)
3066d92bdf4SRichard Henderson{
307*3eedf5ccSRichard Henderson    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
3086d92bdf4SRichard Henderson    gcry_error_t err;
3096d92bdf4SRichard Henderson
3106d92bdf4SRichard Henderson    if (len & (ctx->blocksize - 1)) {
3116d92bdf4SRichard Henderson        error_setg(errp, "Length %zu must be a multiple of block size %zu",
3126d92bdf4SRichard Henderson                   len, ctx->blocksize);
3136d92bdf4SRichard Henderson        return -1;
3146d92bdf4SRichard Henderson    }
3156d92bdf4SRichard Henderson
3166d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
3176d92bdf4SRichard Henderson    if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
3186d92bdf4SRichard Henderson        xts_encrypt(ctx->handle, ctx->tweakhandle,
3196d92bdf4SRichard Henderson                    qcrypto_gcrypt_xts_encrypt,
3206d92bdf4SRichard Henderson                    qcrypto_gcrypt_xts_decrypt,
3216d92bdf4SRichard Henderson                    ctx->iv, len, out, in);
3226d92bdf4SRichard Henderson        return 0;
3236d92bdf4SRichard Henderson    }
3246d92bdf4SRichard Henderson#endif
3256d92bdf4SRichard Henderson
3266d92bdf4SRichard Henderson    err = gcry_cipher_encrypt(ctx->handle,
3276d92bdf4SRichard Henderson                              out, len,
3286d92bdf4SRichard Henderson                              in, len);
3296d92bdf4SRichard Henderson    if (err != 0) {
3306d92bdf4SRichard Henderson        error_setg(errp, "Cannot encrypt data: %s",
3316d92bdf4SRichard Henderson                   gcry_strerror(err));
3326d92bdf4SRichard Henderson        return -1;
3336d92bdf4SRichard Henderson    }
3346d92bdf4SRichard Henderson
3356d92bdf4SRichard Henderson    return 0;
3366d92bdf4SRichard Henderson}
3376d92bdf4SRichard Henderson
3386d92bdf4SRichard Henderson
3396d92bdf4SRichard Hendersonstatic int
3406d92bdf4SRichard Hendersonqcrypto_gcrypt_cipher_decrypt(QCryptoCipher *cipher,
3416d92bdf4SRichard Henderson                              const void *in,
3426d92bdf4SRichard Henderson                              void *out,
3436d92bdf4SRichard Henderson                              size_t len,
3446d92bdf4SRichard Henderson                              Error **errp)
3456d92bdf4SRichard Henderson{
346*3eedf5ccSRichard Henderson    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
3476d92bdf4SRichard Henderson    gcry_error_t err;
3486d92bdf4SRichard Henderson
3496d92bdf4SRichard Henderson    if (len & (ctx->blocksize - 1)) {
3506d92bdf4SRichard Henderson        error_setg(errp, "Length %zu must be a multiple of block size %zu",
3516d92bdf4SRichard Henderson                   len, ctx->blocksize);
3526d92bdf4SRichard Henderson        return -1;
3536d92bdf4SRichard Henderson    }
3546d92bdf4SRichard Henderson
3556d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
3566d92bdf4SRichard Henderson    if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
3576d92bdf4SRichard Henderson        xts_decrypt(ctx->handle, ctx->tweakhandle,
3586d92bdf4SRichard Henderson                    qcrypto_gcrypt_xts_encrypt,
3596d92bdf4SRichard Henderson                    qcrypto_gcrypt_xts_decrypt,
3606d92bdf4SRichard Henderson                    ctx->iv, len, out, in);
3616d92bdf4SRichard Henderson        return 0;
3626d92bdf4SRichard Henderson    }
3636d92bdf4SRichard Henderson#endif
3646d92bdf4SRichard Henderson
3656d92bdf4SRichard Henderson    err = gcry_cipher_decrypt(ctx->handle,
3666d92bdf4SRichard Henderson                              out, len,
3676d92bdf4SRichard Henderson                              in, len);
3686d92bdf4SRichard Henderson    if (err != 0) {
3696d92bdf4SRichard Henderson        error_setg(errp, "Cannot decrypt data: %s",
3706d92bdf4SRichard Henderson                   gcry_strerror(err));
3716d92bdf4SRichard Henderson        return -1;
3726d92bdf4SRichard Henderson    }
3736d92bdf4SRichard Henderson
3746d92bdf4SRichard Henderson    return 0;
3756d92bdf4SRichard Henderson}
3766d92bdf4SRichard Henderson
3776d92bdf4SRichard Hendersonstatic int
3786d92bdf4SRichard Hendersonqcrypto_gcrypt_cipher_setiv(QCryptoCipher *cipher,
3796d92bdf4SRichard Henderson                            const uint8_t *iv, size_t niv,
3806d92bdf4SRichard Henderson                            Error **errp)
3816d92bdf4SRichard Henderson{
382*3eedf5ccSRichard Henderson    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
3836d92bdf4SRichard Henderson    gcry_error_t err;
3846d92bdf4SRichard Henderson
3856d92bdf4SRichard Henderson    if (niv != ctx->blocksize) {
3866d92bdf4SRichard Henderson        error_setg(errp, "Expected IV size %zu not %zu",
3876d92bdf4SRichard Henderson                   ctx->blocksize, niv);
3886d92bdf4SRichard Henderson        return -1;
3896d92bdf4SRichard Henderson    }
3906d92bdf4SRichard Henderson
3916d92bdf4SRichard Henderson#ifdef CONFIG_QEMU_PRIVATE_XTS
3926d92bdf4SRichard Henderson    if (ctx->iv) {
3936d92bdf4SRichard Henderson        memcpy(ctx->iv, iv, niv);
3946d92bdf4SRichard Henderson        return 0;
3956d92bdf4SRichard Henderson    }
3966d92bdf4SRichard Henderson#endif
3976d92bdf4SRichard Henderson
3986d92bdf4SRichard Henderson    if (cipher->mode == QCRYPTO_CIPHER_MODE_CTR) {
3996d92bdf4SRichard Henderson        err = gcry_cipher_setctr(ctx->handle, iv, niv);
4006d92bdf4SRichard Henderson        if (err != 0) {
4016d92bdf4SRichard Henderson            error_setg(errp, "Cannot set Counter: %s",
4026d92bdf4SRichard Henderson                       gcry_strerror(err));
4036d92bdf4SRichard Henderson            return -1;
4046d92bdf4SRichard Henderson        }
4056d92bdf4SRichard Henderson    } else {
4066d92bdf4SRichard Henderson        gcry_cipher_reset(ctx->handle);
4076d92bdf4SRichard Henderson        err = gcry_cipher_setiv(ctx->handle, iv, niv);
4086d92bdf4SRichard Henderson        if (err != 0) {
4096d92bdf4SRichard Henderson            error_setg(errp, "Cannot set IV: %s",
4106d92bdf4SRichard Henderson                       gcry_strerror(err));
4116d92bdf4SRichard Henderson            return -1;
4126d92bdf4SRichard Henderson        }
4136d92bdf4SRichard Henderson    }
4146d92bdf4SRichard Henderson
4156d92bdf4SRichard Henderson    return 0;
4166d92bdf4SRichard Henderson}
4176d92bdf4SRichard Henderson
4186d92bdf4SRichard Henderson
4197b5dbfb7SRichard Hendersonstatic const struct QCryptoCipherDriver qcrypto_cipher_lib_driver = {
4206d92bdf4SRichard Henderson    .cipher_encrypt = qcrypto_gcrypt_cipher_encrypt,
4216d92bdf4SRichard Henderson    .cipher_decrypt = qcrypto_gcrypt_cipher_decrypt,
4226d92bdf4SRichard Henderson    .cipher_setiv = qcrypto_gcrypt_cipher_setiv,
4236d92bdf4SRichard Henderson    .cipher_free = qcrypto_gcrypt_cipher_ctx_free,
4246d92bdf4SRichard Henderson};
425