crypto: ensure XTS is only used with ciphers with 16 byte blocks

The XTS cipher mode needs to be used with a cipher which has
a block size of 16 bytes. If a mis-matching block size is used,
the code will either corrupt memory beyond the IV array, or
not fully encrypt/decrypt the IV.

This fixes a memory corruption crash when attempting to use
cast5-128 with xts, since the former has an 8 byte block size.

A test case is added to ensure the cipher creation fails with
such an invalid combination.

Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrange 2016-08-24 16:28:15 +01:00
parent c2a57aae9a
commit a5d2f44d0d
3 changed files with 48 additions and 13 deletions

View File

@ -192,6 +192,12 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
} }
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { if (cipher->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); ctx->iv = g_new0(uint8_t, ctx->blocksize);
} }

View File

@ -361,6 +361,13 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
goto error; goto error;
} }
if (mode == QCRYPTO_CIPHER_MODE_XTS &&
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); ctx->iv = g_new0(uint8_t, ctx->blocksize);
cipher->opaque = ctx; cipher->opaque = ctx;
@ -456,11 +463,6 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
break; break;
case QCRYPTO_CIPHER_MODE_XTS: case QCRYPTO_CIPHER_MODE_XTS:
if (ctx->blocksize != XTS_BLOCK_SIZE) {
error_setg(errp, "Block size must be %d not %zu",
XTS_BLOCK_SIZE, ctx->blocksize);
return -1;
}
xts_decrypt(ctx->ctx, ctx->ctx_tweak, xts_decrypt(ctx->ctx, ctx->ctx_tweak,
ctx->alg_encrypt_wrapper, ctx->alg_decrypt_wrapper, ctx->alg_encrypt_wrapper, ctx->alg_decrypt_wrapper,
ctx->iv, len, out, in); ctx->iv, len, out, in);

View File

@ -370,6 +370,17 @@ static QCryptoCipherTestData test_data[] = {
"eb4a427d1923ce3ff262735779a418f2" "eb4a427d1923ce3ff262735779a418f2"
"0a282df920147beabe421ee5319d0568", "0a282df920147beabe421ee5319d0568",
}, },
{
/* Bad config - cast5-128 has 8 byte block size
* which is incompatible with XTS
*/
.path = "/crypto/cipher/cast5-xts-128",
.alg = QCRYPTO_CIPHER_ALG_CAST5_128,
.mode = QCRYPTO_CIPHER_MODE_XTS,
.key =
"27182818284590452353602874713526"
"31415926535897932384626433832795",
}
}; };
@ -432,15 +443,23 @@ static void test_cipher(const void *opaque)
const QCryptoCipherTestData *data = opaque; const QCryptoCipherTestData *data = opaque;
QCryptoCipher *cipher; QCryptoCipher *cipher;
uint8_t *key, *iv, *ciphertext, *plaintext, *outtext; uint8_t *key, *iv = NULL, *ciphertext = NULL,
size_t nkey, niv, nciphertext, nplaintext; *plaintext = NULL, *outtext = NULL;
char *outtexthex; size_t nkey, niv = 0, nciphertext = 0, nplaintext = 0;
char *outtexthex = NULL;
size_t ivsize, keysize, blocksize; size_t ivsize, keysize, blocksize;
Error *err = NULL;
nkey = unhex_string(data->key, &key); nkey = unhex_string(data->key, &key);
niv = unhex_string(data->iv, &iv); if (data->iv) {
nciphertext = unhex_string(data->ciphertext, &ciphertext); niv = unhex_string(data->iv, &iv);
nplaintext = unhex_string(data->plaintext, &plaintext); }
if (data->ciphertext) {
nciphertext = unhex_string(data->ciphertext, &ciphertext);
}
if (data->plaintext) {
nplaintext = unhex_string(data->plaintext, &plaintext);
}
g_assert(nciphertext == nplaintext); g_assert(nciphertext == nplaintext);
@ -449,8 +468,15 @@ static void test_cipher(const void *opaque)
cipher = qcrypto_cipher_new( cipher = qcrypto_cipher_new(
data->alg, data->mode, data->alg, data->mode,
key, nkey, key, nkey,
&error_abort); &err);
g_assert(cipher != NULL); if (data->plaintext) {
g_assert(err == NULL);
g_assert(cipher != NULL);
} else {
error_free_or_abort(&err);
g_assert(cipher == NULL);
goto cleanup;
}
keysize = qcrypto_cipher_get_key_len(data->alg); keysize = qcrypto_cipher_get_key_len(data->alg);
blocksize = qcrypto_cipher_get_block_len(data->alg); blocksize = qcrypto_cipher_get_block_len(data->alg);
@ -498,6 +524,7 @@ static void test_cipher(const void *opaque)
g_assert_cmpstr(outtexthex, ==, data->plaintext); g_assert_cmpstr(outtexthex, ==, data->plaintext);
cleanup:
g_free(outtext); g_free(outtext);
g_free(outtexthex); g_free(outtexthex);
g_free(key); g_free(key);