xref: /qemu/crypto/cipher-gcrypt.c.inc (revision a0e93dd8)
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.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 <gcrypt.h>
22
23bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
24                             QCryptoCipherMode mode)
25{
26    switch (alg) {
27    case QCRYPTO_CIPHER_ALG_DES:
28    case QCRYPTO_CIPHER_ALG_3DES:
29    case QCRYPTO_CIPHER_ALG_AES_128:
30    case QCRYPTO_CIPHER_ALG_AES_192:
31    case QCRYPTO_CIPHER_ALG_AES_256:
32    case QCRYPTO_CIPHER_ALG_CAST5_128:
33    case QCRYPTO_CIPHER_ALG_SERPENT_128:
34    case QCRYPTO_CIPHER_ALG_SERPENT_192:
35    case QCRYPTO_CIPHER_ALG_SERPENT_256:
36    case QCRYPTO_CIPHER_ALG_TWOFISH_128:
37    case QCRYPTO_CIPHER_ALG_TWOFISH_256:
38#ifdef CONFIG_CRYPTO_SM4
39    case QCRYPTO_CIPHER_ALG_SM4:
40#endif
41        break;
42    default:
43        return false;
44    }
45
46    switch (mode) {
47    case QCRYPTO_CIPHER_MODE_ECB:
48    case QCRYPTO_CIPHER_MODE_CBC:
49    case QCRYPTO_CIPHER_MODE_XTS:
50    case QCRYPTO_CIPHER_MODE_CTR:
51        return true;
52    default:
53        return false;
54    }
55}
56
57typedef struct QCryptoCipherGcrypt {
58    QCryptoCipher base;
59    gcry_cipher_hd_t handle;
60    size_t blocksize;
61} QCryptoCipherGcrypt;
62
63
64static void qcrypto_gcrypt_ctx_free(QCryptoCipher *cipher)
65{
66    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
67
68    gcry_cipher_close(ctx->handle);
69    g_free(ctx);
70}
71
72static int qcrypto_gcrypt_encrypt(QCryptoCipher *cipher, const void *in,
73                                  void *out, size_t len, Error **errp)
74{
75    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
76    gcry_error_t err;
77
78    if (len & (ctx->blocksize - 1)) {
79        error_setg(errp, "Length %zu must be a multiple of block size %zu",
80                   len, ctx->blocksize);
81        return -1;
82    }
83
84    err = gcry_cipher_encrypt(ctx->handle, out, len, in, len);
85    if (err != 0) {
86        error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err));
87        return -1;
88    }
89
90    return 0;
91}
92
93
94static int qcrypto_gcrypt_decrypt(QCryptoCipher *cipher, const void *in,
95                                  void *out, size_t len, Error **errp)
96{
97    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
98    gcry_error_t err;
99
100    if (len & (ctx->blocksize - 1)) {
101        error_setg(errp, "Length %zu must be a multiple of block size %zu",
102                   len, ctx->blocksize);
103        return -1;
104    }
105
106    err = gcry_cipher_decrypt(ctx->handle, out, len, in, len);
107    if (err != 0) {
108        error_setg(errp, "Cannot decrypt data: %s",
109                   gcry_strerror(err));
110        return -1;
111    }
112
113    return 0;
114}
115
116static int qcrypto_gcrypt_setiv(QCryptoCipher *cipher,
117                                const uint8_t *iv, size_t niv,
118                                Error **errp)
119{
120    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
121    gcry_error_t err;
122
123    if (niv != ctx->blocksize) {
124        error_setg(errp, "Expected IV size %zu not %zu",
125                   ctx->blocksize, niv);
126        return -1;
127    }
128
129    gcry_cipher_reset(ctx->handle);
130    err = gcry_cipher_setiv(ctx->handle, iv, niv);
131    if (err != 0) {
132        error_setg(errp, "Cannot set IV: %s", gcry_strerror(err));
133        return -1;
134    }
135
136    return 0;
137}
138
139static int qcrypto_gcrypt_ctr_setiv(QCryptoCipher *cipher,
140                                    const uint8_t *iv, size_t niv,
141                                    Error **errp)
142{
143    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
144    gcry_error_t err;
145
146    if (niv != ctx->blocksize) {
147        error_setg(errp, "Expected IV size %zu not %zu",
148                   ctx->blocksize, niv);
149        return -1;
150    }
151
152    err = gcry_cipher_setctr(ctx->handle, iv, niv);
153    if (err != 0) {
154        error_setg(errp, "Cannot set Counter: %s", gcry_strerror(err));
155        return -1;
156    }
157
158    return 0;
159}
160
161
162static const struct QCryptoCipherDriver qcrypto_gcrypt_driver = {
163    .cipher_encrypt = qcrypto_gcrypt_encrypt,
164    .cipher_decrypt = qcrypto_gcrypt_decrypt,
165    .cipher_setiv = qcrypto_gcrypt_setiv,
166    .cipher_free = qcrypto_gcrypt_ctx_free,
167};
168
169static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = {
170    .cipher_encrypt = qcrypto_gcrypt_encrypt,
171    .cipher_decrypt = qcrypto_gcrypt_decrypt,
172    .cipher_setiv = qcrypto_gcrypt_ctr_setiv,
173    .cipher_free = qcrypto_gcrypt_ctx_free,
174};
175
176static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
177                                             QCryptoCipherMode mode,
178                                             const uint8_t *key,
179                                             size_t nkey,
180                                             Error **errp)
181{
182    QCryptoCipherGcrypt *ctx;
183    const QCryptoCipherDriver *drv;
184    gcry_error_t err;
185    int gcryalg, gcrymode;
186
187    if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
188        return NULL;
189    }
190
191    switch (alg) {
192    case QCRYPTO_CIPHER_ALG_DES:
193        gcryalg = GCRY_CIPHER_DES;
194        break;
195    case QCRYPTO_CIPHER_ALG_3DES:
196        gcryalg = GCRY_CIPHER_3DES;
197        break;
198    case QCRYPTO_CIPHER_ALG_AES_128:
199        gcryalg = GCRY_CIPHER_AES128;
200        break;
201    case QCRYPTO_CIPHER_ALG_AES_192:
202        gcryalg = GCRY_CIPHER_AES192;
203        break;
204    case QCRYPTO_CIPHER_ALG_AES_256:
205        gcryalg = GCRY_CIPHER_AES256;
206        break;
207    case QCRYPTO_CIPHER_ALG_CAST5_128:
208        gcryalg = GCRY_CIPHER_CAST5;
209        break;
210    case QCRYPTO_CIPHER_ALG_SERPENT_128:
211        gcryalg = GCRY_CIPHER_SERPENT128;
212        break;
213    case QCRYPTO_CIPHER_ALG_SERPENT_192:
214        gcryalg = GCRY_CIPHER_SERPENT192;
215        break;
216    case QCRYPTO_CIPHER_ALG_SERPENT_256:
217        gcryalg = GCRY_CIPHER_SERPENT256;
218        break;
219    case QCRYPTO_CIPHER_ALG_TWOFISH_128:
220        gcryalg = GCRY_CIPHER_TWOFISH128;
221        break;
222    case QCRYPTO_CIPHER_ALG_TWOFISH_256:
223        gcryalg = GCRY_CIPHER_TWOFISH;
224        break;
225#ifdef CONFIG_CRYPTO_SM4
226    case QCRYPTO_CIPHER_ALG_SM4:
227        gcryalg = GCRY_CIPHER_SM4;
228        break;
229#endif
230    default:
231        error_setg(errp, "Unsupported cipher algorithm %s",
232                   QCryptoCipherAlgorithm_str(alg));
233        return NULL;
234    }
235
236    drv = &qcrypto_gcrypt_driver;
237    switch (mode) {
238    case QCRYPTO_CIPHER_MODE_ECB:
239        gcrymode = GCRY_CIPHER_MODE_ECB;
240        break;
241    case QCRYPTO_CIPHER_MODE_XTS:
242        gcrymode = GCRY_CIPHER_MODE_XTS;
243        break;
244    case QCRYPTO_CIPHER_MODE_CBC:
245        gcrymode = GCRY_CIPHER_MODE_CBC;
246        break;
247    case QCRYPTO_CIPHER_MODE_CTR:
248        drv = &qcrypto_gcrypt_ctr_driver;
249        gcrymode = GCRY_CIPHER_MODE_CTR;
250        break;
251    default:
252        error_setg(errp, "Unsupported cipher mode %s",
253                   QCryptoCipherMode_str(mode));
254        return NULL;
255    }
256
257    ctx = g_new0(QCryptoCipherGcrypt, 1);
258    ctx->base.driver = drv;
259
260    err = gcry_cipher_open(&ctx->handle, gcryalg, gcrymode, 0);
261    if (err != 0) {
262        error_setg(errp, "Cannot initialize cipher: %s",
263                   gcry_strerror(err));
264        goto error;
265    }
266    ctx->blocksize = gcry_cipher_get_algo_blklen(gcryalg);
267
268    err = gcry_cipher_setkey(ctx->handle, key, nkey);
269    if (err != 0) {
270        error_setg(errp, "Cannot set key: %s", gcry_strerror(err));
271        goto error;
272    }
273
274    return &ctx->base;
275
276 error:
277    gcry_cipher_close(ctx->handle);
278    g_free(ctx);
279    return NULL;
280}
281