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} 336