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