Commit a5d2f44d authored by Daniel P. Berrangé's avatar Daniel P. Berrangé
Browse files

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: default avatarEric Blake <eblake@redhat.com>
Signed-off-by: default avatarDaniel P. Berrange <berrange@redhat.com>
parent c2a57aae
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -192,6 +192,12 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
    }

    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);
    }

+7 −5
Original line number Diff line number Diff line
@@ -361,6 +361,13 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
        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);
    cipher->opaque = ctx;

@@ -456,11 +463,6 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
        break;

    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,
                    ctx->alg_encrypt_wrapper, ctx->alg_decrypt_wrapper,
                    ctx->iv, len, out, in);
+35 −8
Original line number Diff line number Diff line
@@ -370,6 +370,17 @@ static QCryptoCipherTestData test_data[] = {
            "eb4a427d1923ce3ff262735779a418f2"
            "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;

    QCryptoCipher *cipher;
    uint8_t *key, *iv, *ciphertext, *plaintext, *outtext;
    size_t nkey, niv, nciphertext, nplaintext;
    char *outtexthex;
    uint8_t *key, *iv = NULL, *ciphertext = NULL,
        *plaintext = NULL, *outtext = NULL;
    size_t nkey, niv = 0, nciphertext = 0, nplaintext = 0;
    char *outtexthex = NULL;
    size_t ivsize, keysize, blocksize;
    Error *err = NULL;

    nkey = unhex_string(data->key, &key);
    if (data->iv) {
        niv = unhex_string(data->iv, &iv);
    }
    if (data->ciphertext) {
        nciphertext = unhex_string(data->ciphertext, &ciphertext);
    }
    if (data->plaintext) {
        nplaintext = unhex_string(data->plaintext, &plaintext);
    }

    g_assert(nciphertext == nplaintext);

@@ -449,8 +468,15 @@ static void test_cipher(const void *opaque)
    cipher = qcrypto_cipher_new(
        data->alg, data->mode,
        key, nkey,
        &error_abort);
        &err);
    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);
    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);

 cleanup:
    g_free(outtext);
    g_free(outtexthex);
    g_free(key);