crypto/gcrypt: Split QCryptoCipherGcrypt into subclasses

With gcrypt, most of the dispatch happens in the library,
so there aren't many classes to create.  However, we can
still create separate dispatch for CTR mode, and for
CONFIG_QEMU_PRIVATE_XTS, which avoids needing to check
for these modes at runtime.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Richard Henderson 2020-08-28 10:05:23 -07:00 committed by Daniel P. Berrangé
parent 53ddad9b83
commit 1b010d9339
1 changed files with 240 additions and 257 deletions

View File

@ -24,8 +24,6 @@
#include <gcrypt.h> #include <gcrypt.h>
static const struct QCryptoCipherDriver qcrypto_cipher_lib_driver;
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
QCryptoCipherMode mode) QCryptoCipherMode mode)
{ {
@ -57,36 +55,212 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
} }
} }
typedef struct QCryptoCipherGcrypt QCryptoCipherGcrypt; typedef struct QCryptoCipherGcrypt {
struct QCryptoCipherGcrypt {
QCryptoCipher base; QCryptoCipher base;
gcry_cipher_hd_t handle; gcry_cipher_hd_t handle;
size_t blocksize; size_t blocksize;
#ifdef CONFIG_QEMU_PRIVATE_XTS #ifdef CONFIG_QEMU_PRIVATE_XTS
gcry_cipher_hd_t tweakhandle; gcry_cipher_hd_t tweakhandle;
/* Initialization vector or Counter */ uint8_t iv[XTS_BLOCK_SIZE];
uint8_t *iv;
#endif #endif
}; } QCryptoCipherGcrypt;
static void
qcrypto_gcrypt_cipher_free_ctx(QCryptoCipherGcrypt *ctx, static void qcrypto_gcrypt_ctx_free(QCryptoCipher *cipher)
QCryptoCipherMode mode)
{ {
if (!ctx) { QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
return;
}
gcry_cipher_close(ctx->handle); gcry_cipher_close(ctx->handle);
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
gcry_cipher_close(ctx->tweakhandle);
}
g_free(ctx->iv);
#endif
g_free(ctx); g_free(ctx);
} }
static int qcrypto_gcrypt_encrypt(QCryptoCipher *cipher, const void *in,
void *out, size_t len, Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
gcry_error_t err;
if (len & (ctx->blocksize - 1)) {
error_setg(errp, "Length %zu must be a multiple of block size %zu",
len, ctx->blocksize);
return -1;
}
err = gcry_cipher_encrypt(ctx->handle, out, len, in, len);
if (err != 0) {
error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err));
return -1;
}
return 0;
}
static int qcrypto_gcrypt_decrypt(QCryptoCipher *cipher, const void *in,
void *out, size_t len, Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
gcry_error_t err;
if (len & (ctx->blocksize - 1)) {
error_setg(errp, "Length %zu must be a multiple of block size %zu",
len, ctx->blocksize);
return -1;
}
err = gcry_cipher_decrypt(ctx->handle, out, len, in, len);
if (err != 0) {
error_setg(errp, "Cannot decrypt data: %s",
gcry_strerror(err));
return -1;
}
return 0;
}
static int qcrypto_gcrypt_setiv(QCryptoCipher *cipher,
const uint8_t *iv, size_t niv,
Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
gcry_error_t err;
if (niv != ctx->blocksize) {
error_setg(errp, "Expected IV size %zu not %zu",
ctx->blocksize, niv);
return -1;
}
gcry_cipher_reset(ctx->handle);
err = gcry_cipher_setiv(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set IV: %s", gcry_strerror(err));
return -1;
}
return 0;
}
static int qcrypto_gcrypt_ctr_setiv(QCryptoCipher *cipher,
const uint8_t *iv, size_t niv,
Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
gcry_error_t err;
if (niv != ctx->blocksize) {
error_setg(errp, "Expected IV size %zu not %zu",
ctx->blocksize, niv);
return -1;
}
err = gcry_cipher_setctr(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set Counter: %s", gcry_strerror(err));
return -1;
}
return 0;
}
static const struct QCryptoCipherDriver qcrypto_gcrypt_driver = {
.cipher_encrypt = qcrypto_gcrypt_encrypt,
.cipher_decrypt = qcrypto_gcrypt_decrypt,
.cipher_setiv = qcrypto_gcrypt_setiv,
.cipher_free = qcrypto_gcrypt_ctx_free,
};
static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = {
.cipher_encrypt = qcrypto_gcrypt_encrypt,
.cipher_decrypt = qcrypto_gcrypt_decrypt,
.cipher_setiv = qcrypto_gcrypt_ctr_setiv,
.cipher_free = qcrypto_gcrypt_ctx_free,
};
#ifdef CONFIG_QEMU_PRIVATE_XTS
static void qcrypto_gcrypt_xts_ctx_free(QCryptoCipher *cipher)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
gcry_cipher_close(ctx->tweakhandle);
qcrypto_gcrypt_ctx_free(cipher);
}
static void qcrypto_gcrypt_xts_wrape(const void *ctx, size_t length,
uint8_t *dst, const uint8_t *src)
{
gcry_error_t err;
err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
g_assert(err == 0);
}
static void qcrypto_gcrypt_xts_wrapd(const void *ctx, size_t length,
uint8_t *dst, const uint8_t *src)
{
gcry_error_t err;
err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
g_assert(err == 0);
}
static int qcrypto_gcrypt_xts_encrypt(QCryptoCipher *cipher, const void *in,
void *out, size_t len, Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
if (len & (ctx->blocksize - 1)) {
error_setg(errp, "Length %zu must be a multiple of block size %zu",
len, ctx->blocksize);
return -1;
}
xts_encrypt(ctx->handle, ctx->tweakhandle,
qcrypto_gcrypt_xts_wrape, qcrypto_gcrypt_xts_wrapd,
ctx->iv, len, out, in);
return 0;
}
static int qcrypto_gcrypt_xts_decrypt(QCryptoCipher *cipher, const void *in,
void *out, size_t len, Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
if (len & (ctx->blocksize - 1)) {
error_setg(errp, "Length %zu must be a multiple of block size %zu",
len, ctx->blocksize);
return -1;
}
xts_decrypt(ctx->handle, ctx->tweakhandle,
qcrypto_gcrypt_xts_wrape, qcrypto_gcrypt_xts_wrapd,
ctx->iv, len, out, in);
return 0;
}
static int qcrypto_gcrypt_xts_setiv(QCryptoCipher *cipher,
const uint8_t *iv, size_t niv,
Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
if (niv != ctx->blocksize) {
error_setg(errp, "Expected IV size %zu not %zu",
ctx->blocksize, niv);
return -1;
}
memcpy(ctx->iv, iv, niv);
return 0;
}
static const struct QCryptoCipherDriver qcrypto_gcrypt_xts_driver = {
.cipher_encrypt = qcrypto_gcrypt_xts_encrypt,
.cipher_decrypt = qcrypto_gcrypt_xts_decrypt,
.cipher_setiv = qcrypto_gcrypt_xts_setiv,
.cipher_free = qcrypto_gcrypt_xts_ctx_free,
};
#endif /* CONFIG_QEMU_PRIVATE_XTS */
static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
QCryptoCipherMode mode, QCryptoCipherMode mode,
@ -95,32 +269,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
Error **errp) Error **errp)
{ {
QCryptoCipherGcrypt *ctx; QCryptoCipherGcrypt *ctx;
const QCryptoCipherDriver *drv;
gcry_error_t err; gcry_error_t err;
int gcryalg, gcrymode; int gcryalg, gcrymode;
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
gcrymode = GCRY_CIPHER_MODE_ECB;
break;
case QCRYPTO_CIPHER_MODE_XTS:
#ifdef CONFIG_QEMU_PRIVATE_XTS
gcrymode = GCRY_CIPHER_MODE_ECB;
#else
gcrymode = GCRY_CIPHER_MODE_XTS;
#endif
break;
case QCRYPTO_CIPHER_MODE_CBC:
gcrymode = GCRY_CIPHER_MODE_CBC;
break;
case QCRYPTO_CIPHER_MODE_CTR:
gcrymode = GCRY_CIPHER_MODE_CTR;
break;
default:
error_setg(errp, "Unsupported cipher mode %s",
QCryptoCipherMode_str(mode));
return NULL;
}
if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
return NULL; return NULL;
} }
@ -129,54 +281,70 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
case QCRYPTO_CIPHER_ALG_DES_RFB: case QCRYPTO_CIPHER_ALG_DES_RFB:
gcryalg = GCRY_CIPHER_DES; gcryalg = GCRY_CIPHER_DES;
break; break;
case QCRYPTO_CIPHER_ALG_3DES: case QCRYPTO_CIPHER_ALG_3DES:
gcryalg = GCRY_CIPHER_3DES; gcryalg = GCRY_CIPHER_3DES;
break; break;
case QCRYPTO_CIPHER_ALG_AES_128: case QCRYPTO_CIPHER_ALG_AES_128:
gcryalg = GCRY_CIPHER_AES128; gcryalg = GCRY_CIPHER_AES128;
break; break;
case QCRYPTO_CIPHER_ALG_AES_192: case QCRYPTO_CIPHER_ALG_AES_192:
gcryalg = GCRY_CIPHER_AES192; gcryalg = GCRY_CIPHER_AES192;
break; break;
case QCRYPTO_CIPHER_ALG_AES_256: case QCRYPTO_CIPHER_ALG_AES_256:
gcryalg = GCRY_CIPHER_AES256; gcryalg = GCRY_CIPHER_AES256;
break; break;
case QCRYPTO_CIPHER_ALG_CAST5_128: case QCRYPTO_CIPHER_ALG_CAST5_128:
gcryalg = GCRY_CIPHER_CAST5; gcryalg = GCRY_CIPHER_CAST5;
break; break;
case QCRYPTO_CIPHER_ALG_SERPENT_128: case QCRYPTO_CIPHER_ALG_SERPENT_128:
gcryalg = GCRY_CIPHER_SERPENT128; gcryalg = GCRY_CIPHER_SERPENT128;
break; break;
case QCRYPTO_CIPHER_ALG_SERPENT_192: case QCRYPTO_CIPHER_ALG_SERPENT_192:
gcryalg = GCRY_CIPHER_SERPENT192; gcryalg = GCRY_CIPHER_SERPENT192;
break; break;
case QCRYPTO_CIPHER_ALG_SERPENT_256: case QCRYPTO_CIPHER_ALG_SERPENT_256:
gcryalg = GCRY_CIPHER_SERPENT256; gcryalg = GCRY_CIPHER_SERPENT256;
break; break;
case QCRYPTO_CIPHER_ALG_TWOFISH_128: case QCRYPTO_CIPHER_ALG_TWOFISH_128:
gcryalg = GCRY_CIPHER_TWOFISH128; gcryalg = GCRY_CIPHER_TWOFISH128;
break; break;
case QCRYPTO_CIPHER_ALG_TWOFISH_256: case QCRYPTO_CIPHER_ALG_TWOFISH_256:
gcryalg = GCRY_CIPHER_TWOFISH; gcryalg = GCRY_CIPHER_TWOFISH;
break; break;
default: default:
error_setg(errp, "Unsupported cipher algorithm %s", error_setg(errp, "Unsupported cipher algorithm %s",
QCryptoCipherAlgorithm_str(alg)); QCryptoCipherAlgorithm_str(alg));
return NULL; return NULL;
} }
drv = &qcrypto_gcrypt_driver;
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
gcrymode = GCRY_CIPHER_MODE_ECB;
break;
case QCRYPTO_CIPHER_MODE_XTS:
#ifdef CONFIG_QEMU_PRIVATE_XTS
drv = &qcrypto_gcrypt_xts_driver;
gcrymode = GCRY_CIPHER_MODE_ECB;
#else
gcrymode = GCRY_CIPHER_MODE_XTS;
#endif
break;
case QCRYPTO_CIPHER_MODE_CBC:
gcrymode = GCRY_CIPHER_MODE_CBC;
break;
case QCRYPTO_CIPHER_MODE_CTR:
drv = &qcrypto_gcrypt_ctr_driver;
gcrymode = GCRY_CIPHER_MODE_CTR;
break;
default:
error_setg(errp, "Unsupported cipher mode %s",
QCryptoCipherMode_str(mode));
return NULL;
}
ctx = g_new0(QCryptoCipherGcrypt, 1); ctx = g_new0(QCryptoCipherGcrypt, 1);
ctx->base.driver = drv;
err = gcry_cipher_open(&ctx->handle, gcryalg, gcrymode, 0); err = gcry_cipher_open(&ctx->handle, gcryalg, gcrymode, 0);
if (err != 0) { if (err != 0) {
@ -184,8 +352,16 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
gcry_strerror(err)); gcry_strerror(err));
goto error; goto error;
} }
ctx->blocksize = gcry_cipher_get_algo_blklen(gcryalg);
#ifdef CONFIG_QEMU_PRIVATE_XTS #ifdef CONFIG_QEMU_PRIVATE_XTS
if (mode == QCRYPTO_CIPHER_MODE_XTS) { if (mode == QCRYPTO_CIPHER_MODE_XTS) {
if (ctx->blocksize != XTS_BLOCK_SIZE) {
error_setg(errp,
"Cipher block size %zu must equal XTS block size %d",
ctx->blocksize, XTS_BLOCK_SIZE);
goto error;
}
err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0); err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0);
if (err != 0) { if (err != 0) {
error_setg(errp, "Cannot initialize cipher: %s", error_setg(errp, "Cannot initialize cipher: %s",
@ -203,224 +379,31 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey); err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey);
g_free(rfbkey); g_free(rfbkey);
ctx->blocksize = 8;
} else { } else {
#ifdef CONFIG_QEMU_PRIVATE_XTS #ifdef CONFIG_QEMU_PRIVATE_XTS
if (mode == QCRYPTO_CIPHER_MODE_XTS) { if (mode == QCRYPTO_CIPHER_MODE_XTS) {
nkey /= 2; nkey /= 2;
err = gcry_cipher_setkey(ctx->handle, key, nkey); err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey);
if (err != 0) { if (err != 0) {
error_setg(errp, "Cannot set key: %s", error_setg(errp, "Cannot set key: %s", gcry_strerror(err));
gcry_strerror(err));
goto error; goto error;
} }
err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey);
} else {
#endif
err = gcry_cipher_setkey(ctx->handle, key, nkey);
#ifdef CONFIG_QEMU_PRIVATE_XTS
} }
#endif #endif
if (err != 0) { err = gcry_cipher_setkey(ctx->handle, key, nkey);
error_setg(errp, "Cannot set key: %s",
gcry_strerror(err));
goto error;
}
switch (alg) {
case QCRYPTO_CIPHER_ALG_AES_128:
case QCRYPTO_CIPHER_ALG_AES_192:
case QCRYPTO_CIPHER_ALG_AES_256:
case QCRYPTO_CIPHER_ALG_SERPENT_128:
case QCRYPTO_CIPHER_ALG_SERPENT_192:
case QCRYPTO_CIPHER_ALG_SERPENT_256:
case QCRYPTO_CIPHER_ALG_TWOFISH_128:
case QCRYPTO_CIPHER_ALG_TWOFISH_256:
ctx->blocksize = 16;
break;
case QCRYPTO_CIPHER_ALG_3DES:
case QCRYPTO_CIPHER_ALG_CAST5_128:
ctx->blocksize = 8;
break;
default:
g_assert_not_reached();
}
} }
g_assert(is_power_of_2(ctx->blocksize)); if (err != 0) {
error_setg(errp, "Cannot set key: %s", gcry_strerror(err));
#ifdef CONFIG_QEMU_PRIVATE_XTS goto error;
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
if (ctx->blocksize != XTS_BLOCK_SIZE) {
error_setg(errp,
"Cipher block size %zu must equal XTS block size %d",
ctx->blocksize, XTS_BLOCK_SIZE);
goto error;
}
ctx->iv = g_new0(uint8_t, ctx->blocksize);
} }
#endif
ctx->base.driver = &qcrypto_cipher_lib_driver;
return &ctx->base; return &ctx->base;
error: error:
qcrypto_gcrypt_cipher_free_ctx(ctx, mode); #ifdef CONFIG_QEMU_PRIVATE_XTS
gcry_cipher_close(ctx->tweakhandle);
#endif
gcry_cipher_close(ctx->handle);
g_free(ctx);
return NULL; return NULL;
} }
static void
qcrypto_gcrypt_cipher_ctx_free(QCryptoCipher *cipher)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
qcrypto_gcrypt_cipher_free_ctx(ctx, cipher->mode);
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
static void qcrypto_gcrypt_xts_encrypt(const void *ctx,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
gcry_error_t err;
err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
g_assert(err == 0);
}
static void qcrypto_gcrypt_xts_decrypt(const void *ctx,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
gcry_error_t err;
err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
g_assert(err == 0);
}
#endif
static int
qcrypto_gcrypt_cipher_encrypt(QCryptoCipher *cipher,
const void *in,
void *out,
size_t len,
Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
gcry_error_t err;
if (len & (ctx->blocksize - 1)) {
error_setg(errp, "Length %zu must be a multiple of block size %zu",
len, ctx->blocksize);
return -1;
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
xts_encrypt(ctx->handle, ctx->tweakhandle,
qcrypto_gcrypt_xts_encrypt,
qcrypto_gcrypt_xts_decrypt,
ctx->iv, len, out, in);
return 0;
}
#endif
err = gcry_cipher_encrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot encrypt data: %s",
gcry_strerror(err));
return -1;
}
return 0;
}
static int
qcrypto_gcrypt_cipher_decrypt(QCryptoCipher *cipher,
const void *in,
void *out,
size_t len,
Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
gcry_error_t err;
if (len & (ctx->blocksize - 1)) {
error_setg(errp, "Length %zu must be a multiple of block size %zu",
len, ctx->blocksize);
return -1;
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
xts_decrypt(ctx->handle, ctx->tweakhandle,
qcrypto_gcrypt_xts_encrypt,
qcrypto_gcrypt_xts_decrypt,
ctx->iv, len, out, in);
return 0;
}
#endif
err = gcry_cipher_decrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot decrypt data: %s",
gcry_strerror(err));
return -1;
}
return 0;
}
static int
qcrypto_gcrypt_cipher_setiv(QCryptoCipher *cipher,
const uint8_t *iv, size_t niv,
Error **errp)
{
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
gcry_error_t err;
if (niv != ctx->blocksize) {
error_setg(errp, "Expected IV size %zu not %zu",
ctx->blocksize, niv);
return -1;
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (ctx->iv) {
memcpy(ctx->iv, iv, niv);
return 0;
}
#endif
if (cipher->mode == QCRYPTO_CIPHER_MODE_CTR) {
err = gcry_cipher_setctr(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set Counter: %s",
gcry_strerror(err));
return -1;
}
} else {
gcry_cipher_reset(ctx->handle);
err = gcry_cipher_setiv(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set IV: %s",
gcry_strerror(err));
return -1;
}
}
return 0;
}
static const struct QCryptoCipherDriver qcrypto_cipher_lib_driver = {
.cipher_encrypt = qcrypto_gcrypt_cipher_encrypt,
.cipher_decrypt = qcrypto_gcrypt_cipher_decrypt,
.cipher_setiv = qcrypto_gcrypt_cipher_setiv,
.cipher_free = qcrypto_gcrypt_cipher_ctx_free,
};