@samitouri / QOSamiQemu / commits / 1429ef61c8

crypto/cipher-gcrypt: Implement AES-GCM

Map QCRYPTO_CIPHER_MODE_GCM to GCRY_CIPHER_MODE_GCM and advertise it in qcrypto_cipher_supports() for 128-bit block ciphers. Add a GCM driver whose setiv accepts the (typically 96-bit) nonce, whose encrypt/decrypt do not require block-aligned lengths, and which implements setaad via gcry_cipher_authenticate() and gettag via gcry_cipher_gettag(). Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Acked-by: Daniel P. Berrangé <berrange@redhat.com> Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater <clg@redhat.com>

Jamin Lin committed Aug 11, 2026 at 06:01 UTC 1429ef61c89672ef8c8eb3241c22ba46390b9003
1 file changed +101
crypto/cipher-gcrypt.c.inc
+101
@@ -65,6 +65,8 @@ static int qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode)
65 return GCRY_CIPHER_MODE_CBC;
66 case QCRYPTO_CIPHER_MODE_CTR:
67 return GCRY_CIPHER_MODE_CTR;
68 + case QCRYPTO_CIPHER_MODE_GCM:
69 + return GCRY_CIPHER_MODE_GCM;
70 default:
71 return GCRY_CIPHER_MODE_NONE;
72 }
@@ -104,6 +106,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
106 case QCRYPTO_CIPHER_MODE_XTS:
107 case QCRYPTO_CIPHER_MODE_CTR:
108 return true;
109 + case QCRYPTO_CIPHER_MODE_GCM:
110 + /* GCM requires a 128-bit block cipher. */
111 + return gcry_cipher_get_algo_blklen(
112 + qcrypto_cipher_alg_to_gcry_alg(alg)) == 16;
113 default:
114 return false;
115 }
@@ -228,6 +234,99 @@ static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = {
234 .cipher_free = qcrypto_gcrypt_ctx_free,
235 };
236
237 +/*
238 + * GCM is an AEAD stream mode: the IV/nonce need not match the block size,
239 + * the message length need not be a multiple of the block size, associated
240 + * data is fed with gcry_cipher_authenticate() and the authentication tag is
241 + * read back with gcry_cipher_gettag().
242 + */
243 +static int qcrypto_gcrypt_gcm_setiv(QCryptoCipher *cipher,
244 + const uint8_t *iv, size_t niv,
245 + Error **errp)
246 +{
247 + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
248 + gcry_error_t err;
249 +
250 + gcry_cipher_reset(ctx->handle);
251 + err = gcry_cipher_setiv(ctx->handle, iv, niv);
252 + if (err != 0) {
253 + error_setg(errp, "Cannot set IV: %s", gcry_strerror(err));
254 + return -1;
255 + }
256 +
257 + return 0;
258 +}
259 +
260 +static int qcrypto_gcrypt_gcm_setaad(QCryptoCipher *cipher,
261 + const uint8_t *aad, size_t len,
262 + Error **errp)
263 +{
264 + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
265 + gcry_error_t err;
266 +
267 + err = gcry_cipher_authenticate(ctx->handle, aad, len);
268 + if (err != 0) {
269 + error_setg(errp, "Cannot set AAD: %s", gcry_strerror(err));
270 + return -1;
271 + }
272 +
273 + return 0;
274 +}
275 +
276 +static int qcrypto_gcrypt_gcm_encrypt(QCryptoCipher *cipher, const void *in,
277 + void *out, size_t len, Error **errp)
278 +{
279 + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
280 + gcry_error_t err;
281 +
282 + err = gcry_cipher_encrypt(ctx->handle, out, len, in, len);
283 + if (err != 0) {
284 + error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err));
285 + return -1;
286 + }
287 +
288 + return 0;
289 +}
290 +
291 +static int qcrypto_gcrypt_gcm_decrypt(QCryptoCipher *cipher, const void *in,
292 + void *out, size_t len, Error **errp)
293 +{
294 + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
295 + gcry_error_t err;
296 +
297 + err = gcry_cipher_decrypt(ctx->handle, out, len, in, len);
298 + if (err != 0) {
299 + error_setg(errp, "Cannot decrypt data: %s", gcry_strerror(err));
300 + return -1;
301 + }
302 +
303 + return 0;
304 +}
305 +
306 +static int qcrypto_gcrypt_gcm_gettag(QCryptoCipher *cipher,
307 + uint8_t *tag, size_t len, Error **errp)
308 +{
309 + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
310 + gcry_error_t err;
311 +
312 + err = gcry_cipher_gettag(ctx->handle, tag, len);
313 + if (err != 0) {
314 + error_setg(errp, "Cannot get tag: %s", gcry_strerror(err));
315 + return -1;
316 + }
317 +
318 + return 0;
319 +}
320 +
321 +static const struct QCryptoCipherDriver qcrypto_gcrypt_gcm_driver = {
322 + .cipher_encrypt = qcrypto_gcrypt_gcm_encrypt,
323 + .cipher_decrypt = qcrypto_gcrypt_gcm_decrypt,
324 + .cipher_setiv = qcrypto_gcrypt_gcm_setiv,
325 + .cipher_setaad = qcrypto_gcrypt_gcm_setaad,
326 + .cipher_gettag = qcrypto_gcrypt_gcm_gettag,
327 + .cipher_free = qcrypto_gcrypt_ctx_free,
328 +};
329 +
330 static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
331 QCryptoCipherMode mode,
332 const uint8_t *key,
@@ -259,6 +358,8 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
358
359 if (mode == QCRYPTO_CIPHER_MODE_CTR) {
360 drv = &qcrypto_gcrypt_ctr_driver;
361 + } else if (mode == QCRYPTO_CIPHER_MODE_GCM) {
362 + drv = &qcrypto_gcrypt_gcm_driver;
363 } else {
364 drv = &qcrypto_gcrypt_driver;
365 }