qemu/tests/test-crypto-block.c
<<
>>
Prefs
   1/*
   2 * QEMU Crypto block encryption
   3 *
   4 * Copyright (c) 2016 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 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 "qapi/error.h"
  23#include "crypto/init.h"
  24#include "crypto/block.h"
  25#include "qemu/buffer.h"
  26#include "crypto/secret.h"
  27#ifndef _WIN32
  28#include <sys/resource.h>
  29#endif
  30
  31#if (defined(_WIN32) || defined RUSAGE_THREAD) && \
  32    (defined(CONFIG_NETTLE_KDF) || defined(CONFIG_GCRYPT_KDF))
  33#define TEST_LUKS
  34#else
  35#undef TEST_LUKS
  36#endif
  37
  38static QCryptoBlockCreateOptions qcow_create_opts = {
  39    .format = Q_CRYPTO_BLOCK_FORMAT_QCOW,
  40    .u.qcow = {
  41        .has_key_secret = true,
  42        .key_secret = (char *)"sec0",
  43    },
  44};
  45
  46static QCryptoBlockOpenOptions qcow_open_opts = {
  47    .format = Q_CRYPTO_BLOCK_FORMAT_QCOW,
  48    .u.qcow = {
  49        .has_key_secret = true,
  50        .key_secret = (char *)"sec0",
  51    },
  52};
  53
  54
  55#ifdef TEST_LUKS
  56static QCryptoBlockOpenOptions luks_open_opts = {
  57    .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
  58    .u.luks = {
  59        .has_key_secret = true,
  60        .key_secret = (char *)"sec0",
  61    },
  62};
  63
  64
  65/* Creation with all default values */
  66static QCryptoBlockCreateOptions luks_create_opts_default = {
  67    .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
  68    .u.luks = {
  69        .has_key_secret = true,
  70        .key_secret = (char *)"sec0",
  71    },
  72};
  73
  74
  75/* ...and with explicit values */
  76static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = {
  77    .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
  78    .u.luks = {
  79        .has_key_secret = true,
  80        .key_secret = (char *)"sec0",
  81        .has_cipher_alg = true,
  82        .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
  83        .has_cipher_mode = true,
  84        .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
  85        .has_ivgen_alg = true,
  86        .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64,
  87    },
  88};
  89
  90
  91static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_essiv = {
  92    .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
  93    .u.luks = {
  94        .has_key_secret = true,
  95        .key_secret = (char *)"sec0",
  96        .has_cipher_alg = true,
  97        .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
  98        .has_cipher_mode = true,
  99        .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
 100        .has_ivgen_alg = true,
 101        .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV,
 102        .has_ivgen_hash_alg = true,
 103        .ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256,
 104        .has_hash_alg = true,
 105        .hash_alg = QCRYPTO_HASH_ALG_SHA1,
 106    },
 107};
 108#endif /* TEST_LUKS */
 109
 110
 111static struct QCryptoBlockTestData {
 112    const char *path;
 113    QCryptoBlockCreateOptions *create_opts;
 114    QCryptoBlockOpenOptions *open_opts;
 115
 116    bool expect_header;
 117
 118    QCryptoCipherAlgorithm cipher_alg;
 119    QCryptoCipherMode cipher_mode;
 120    QCryptoHashAlgorithm hash_alg;
 121
 122    QCryptoIVGenAlgorithm ivgen_alg;
 123    QCryptoHashAlgorithm ivgen_hash;
 124
 125    bool slow;
 126} test_data[] = {
 127    {
 128        .path = "/crypto/block/qcow",
 129        .create_opts = &qcow_create_opts,
 130        .open_opts = &qcow_open_opts,
 131
 132        .expect_header = false,
 133
 134        .cipher_alg = QCRYPTO_CIPHER_ALG_AES_128,
 135        .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
 136
 137        .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64,
 138    },
 139#ifdef TEST_LUKS
 140    {
 141        .path = "/crypto/block/luks/default",
 142        .create_opts = &luks_create_opts_default,
 143        .open_opts = &luks_open_opts,
 144
 145        .expect_header = true,
 146
 147        .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
 148        .cipher_mode = QCRYPTO_CIPHER_MODE_XTS,
 149        .hash_alg = QCRYPTO_HASH_ALG_SHA256,
 150
 151        .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64,
 152
 153        .slow = true,
 154    },
 155    {
 156        .path = "/crypto/block/luks/aes-256-cbc-plain64",
 157        .create_opts = &luks_create_opts_aes256_cbc_plain64,
 158        .open_opts = &luks_open_opts,
 159
 160        .expect_header = true,
 161
 162        .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
 163        .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
 164        .hash_alg = QCRYPTO_HASH_ALG_SHA256,
 165
 166        .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64,
 167
 168        .slow = true,
 169    },
 170    {
 171        .path = "/crypto/block/luks/aes-256-cbc-essiv",
 172        .create_opts = &luks_create_opts_aes256_cbc_essiv,
 173        .open_opts = &luks_open_opts,
 174
 175        .expect_header = true,
 176
 177        .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
 178        .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
 179        .hash_alg = QCRYPTO_HASH_ALG_SHA1,
 180
 181        .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV,
 182        .ivgen_hash = QCRYPTO_HASH_ALG_SHA256,
 183
 184        .slow = true,
 185    },
 186#endif
 187};
 188
 189
 190static ssize_t test_block_read_func(QCryptoBlock *block,
 191                                    size_t offset,
 192                                    uint8_t *buf,
 193                                    size_t buflen,
 194                                    void *opaque,
 195                                    Error **errp)
 196{
 197    Buffer *header = opaque;
 198
 199    g_assert_cmpint(offset + buflen, <=, header->capacity);
 200
 201    memcpy(buf, header->buffer + offset, buflen);
 202
 203    return buflen;
 204}
 205
 206
 207static ssize_t test_block_init_func(QCryptoBlock *block,
 208                                    size_t headerlen,
 209                                    void *opaque,
 210                                    Error **errp)
 211{
 212    Buffer *header = opaque;
 213
 214    g_assert_cmpint(header->capacity, ==, 0);
 215
 216    buffer_reserve(header, headerlen);
 217
 218    return headerlen;
 219}
 220
 221
 222static ssize_t test_block_write_func(QCryptoBlock *block,
 223                                     size_t offset,
 224                                     const uint8_t *buf,
 225                                     size_t buflen,
 226                                     void *opaque,
 227                                     Error **errp)
 228{
 229    Buffer *header = opaque;
 230
 231    g_assert_cmpint(buflen + offset, <=, header->capacity);
 232
 233    memcpy(header->buffer + offset, buf, buflen);
 234    header->offset = offset + buflen;
 235
 236    return buflen;
 237}
 238
 239
 240static Object *test_block_secret(void)
 241{
 242    return object_new_with_props(
 243        TYPE_QCRYPTO_SECRET,
 244        object_get_objects_root(),
 245        "sec0",
 246        &error_abort,
 247        "data", "123456",
 248        NULL);
 249}
 250
 251static void test_block_assert_setup(const struct QCryptoBlockTestData *data,
 252                                    QCryptoBlock *blk)
 253{
 254    QCryptoIVGen *ivgen;
 255    QCryptoCipher *cipher;
 256
 257    ivgen = qcrypto_block_get_ivgen(blk);
 258    cipher = qcrypto_block_get_cipher(blk);
 259
 260    g_assert(ivgen);
 261    g_assert(cipher);
 262
 263    g_assert_cmpint(data->cipher_alg, ==, cipher->alg);
 264    g_assert_cmpint(data->cipher_mode, ==, cipher->mode);
 265    g_assert_cmpint(data->hash_alg, ==,
 266                    qcrypto_block_get_kdf_hash(blk));
 267
 268    g_assert_cmpint(data->ivgen_alg, ==,
 269                    qcrypto_ivgen_get_algorithm(ivgen));
 270    g_assert_cmpint(data->ivgen_hash, ==,
 271                    qcrypto_ivgen_get_hash(ivgen));
 272}
 273
 274
 275static void test_block(gconstpointer opaque)
 276{
 277    const struct QCryptoBlockTestData *data = opaque;
 278    QCryptoBlock *blk;
 279    Buffer header;
 280    Object *sec = test_block_secret();
 281
 282    memset(&header, 0, sizeof(header));
 283    buffer_init(&header, "header");
 284
 285    blk = qcrypto_block_create(data->create_opts, NULL,
 286                               test_block_init_func,
 287                               test_block_write_func,
 288                               &header,
 289                               &error_abort);
 290    g_assert(blk);
 291
 292    if (data->expect_header) {
 293        g_assert_cmpint(header.capacity, >, 0);
 294    } else {
 295        g_assert_cmpint(header.capacity, ==, 0);
 296    }
 297
 298    test_block_assert_setup(data, blk);
 299
 300    qcrypto_block_free(blk);
 301    object_unparent(sec);
 302
 303    /* Ensure we can't open without the secret */
 304    blk = qcrypto_block_open(data->open_opts, NULL,
 305                             test_block_read_func,
 306                             &header,
 307                             0,
 308                             NULL);
 309    g_assert(blk == NULL);
 310
 311    /* Ensure we can't open without the secret, unless NO_IO */
 312    blk = qcrypto_block_open(data->open_opts, NULL,
 313                             test_block_read_func,
 314                             &header,
 315                             QCRYPTO_BLOCK_OPEN_NO_IO,
 316                             &error_abort);
 317
 318    g_assert(qcrypto_block_get_cipher(blk) == NULL);
 319    g_assert(qcrypto_block_get_ivgen(blk) == NULL);
 320
 321    qcrypto_block_free(blk);
 322
 323
 324    /* Now open for real with secret */
 325    sec = test_block_secret();
 326    blk = qcrypto_block_open(data->open_opts, NULL,
 327                             test_block_read_func,
 328                             &header,
 329                             0,
 330                             &error_abort);
 331    g_assert(blk);
 332
 333    test_block_assert_setup(data, blk);
 334
 335    qcrypto_block_free(blk);
 336
 337    object_unparent(sec);
 338
 339    buffer_free(&header);
 340}
 341
 342
 343int main(int argc, char **argv)
 344{
 345    gsize i;
 346
 347    module_call_init(MODULE_INIT_QOM);
 348    g_test_init(&argc, &argv, NULL);
 349
 350    g_assert(qcrypto_init(NULL) == 0);
 351
 352    for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
 353        if (test_data[i].open_opts->format == Q_CRYPTO_BLOCK_FORMAT_LUKS &&
 354            !qcrypto_hash_supports(test_data[i].hash_alg)) {
 355            continue;
 356        }
 357        if (!test_data[i].slow ||
 358            g_test_slow()) {
 359            g_test_add_data_func(test_data[i].path, &test_data[i], test_block);
 360        }
 361    }
 362
 363    return g_test_run();
 364}
 365