crypto: add block encryption framework
Add a generic framework for supporting different block encryption formats. Upon instantiating a QCryptoBlock object, it will read the encryption header and extract the encryption keys. It is then possible to call methods to encrypt/decrypt data buffers. There is also a mode whereby it will create/initialize a new encryption header on a previously unformatted volume. The initial framework comes with support for the legacy QCow AES based encryption. This enables code in the QCow driver to be consolidated later. Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
parent
eaec903c5b
commit
7d9690148a
@ -19,6 +19,8 @@ crypto-obj-y += ivgen-plain.o
|
||||
crypto-obj-y += ivgen-plain64.o
|
||||
crypto-obj-y += afsplit.o
|
||||
crypto-obj-y += xts.o
|
||||
crypto-obj-y += block.o
|
||||
crypto-obj-y += block-qcow.o
|
||||
|
||||
# Let the userspace emulators avoid linking gnutls/etc
|
||||
crypto-aes-obj-y = aes.o
|
||||
|
173
crypto/block-qcow.c
Normal file
173
crypto/block-qcow.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* QEMU Crypto block device encryption QCow/QCow2 AES-CBC format
|
||||
*
|
||||
* Copyright (c) 2015-2016 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note that the block encryption implemented in this file is broken
|
||||
* by design. This exists only to allow data to be liberated from
|
||||
* existing qcow[2] images and should not be used in any new areas.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "crypto/block-qcow.h"
|
||||
#include "crypto/secret.h"
|
||||
|
||||
#define QCRYPTO_BLOCK_QCOW_SECTOR_SIZE 512
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED,
|
||||
size_t buf_size G_GNUC_UNUSED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_block_qcow_init(QCryptoBlock *block,
|
||||
const char *keysecret,
|
||||
Error **errp)
|
||||
{
|
||||
char *password;
|
||||
int ret;
|
||||
uint8_t keybuf[16];
|
||||
int len;
|
||||
|
||||
memset(keybuf, 0, 16);
|
||||
|
||||
password = qcrypto_secret_lookup_as_utf8(keysecret, errp);
|
||||
if (!password) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = strlen(password);
|
||||
memcpy(keybuf, password, MIN(len, sizeof(keybuf)));
|
||||
g_free(password);
|
||||
|
||||
block->niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128,
|
||||
QCRYPTO_CIPHER_MODE_CBC);
|
||||
block->ivgen = qcrypto_ivgen_new(QCRYPTO_IVGEN_ALG_PLAIN64,
|
||||
0, 0, NULL, 0, errp);
|
||||
if (!block->ivgen) {
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
block->cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128,
|
||||
QCRYPTO_CIPHER_MODE_CBC,
|
||||
keybuf, G_N_ELEMENTS(keybuf),
|
||||
errp);
|
||||
if (!block->cipher) {
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
block->payload_offset = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
qcrypto_cipher_free(block->cipher);
|
||||
qcrypto_ivgen_free(block->ivgen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_block_qcow_open(QCryptoBlock *block,
|
||||
QCryptoBlockOpenOptions *options,
|
||||
QCryptoBlockReadFunc readfunc G_GNUC_UNUSED,
|
||||
void *opaque G_GNUC_UNUSED,
|
||||
unsigned int flags,
|
||||
Error **errp)
|
||||
{
|
||||
if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) {
|
||||
return 0;
|
||||
} else {
|
||||
if (!options->u.qcow.key_secret) {
|
||||
error_setg(errp,
|
||||
"Parameter 'key-secret' is required for cipher");
|
||||
return -1;
|
||||
}
|
||||
return qcrypto_block_qcow_init(block,
|
||||
options->u.qcow.key_secret, errp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_block_qcow_create(QCryptoBlock *block,
|
||||
QCryptoBlockCreateOptions *options,
|
||||
QCryptoBlockInitFunc initfunc G_GNUC_UNUSED,
|
||||
QCryptoBlockWriteFunc writefunc G_GNUC_UNUSED,
|
||||
void *opaque G_GNUC_UNUSED,
|
||||
Error **errp)
|
||||
{
|
||||
if (!options->u.qcow.key_secret) {
|
||||
error_setg(errp, "Parameter 'key-secret' is required for cipher");
|
||||
return -1;
|
||||
}
|
||||
/* QCow2 has no special header, since everything is hardwired */
|
||||
return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, errp);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_block_qcow_cleanup(QCryptoBlock *block)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_block_qcow_decrypt(QCryptoBlock *block,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
return qcrypto_block_decrypt_helper(block->cipher,
|
||||
block->niv, block->ivgen,
|
||||
QCRYPTO_BLOCK_QCOW_SECTOR_SIZE,
|
||||
startsector, buf, len, errp);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_block_qcow_encrypt(QCryptoBlock *block,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
return qcrypto_block_encrypt_helper(block->cipher,
|
||||
block->niv, block->ivgen,
|
||||
QCRYPTO_BLOCK_QCOW_SECTOR_SIZE,
|
||||
startsector, buf, len, errp);
|
||||
}
|
||||
|
||||
|
||||
const QCryptoBlockDriver qcrypto_block_driver_qcow = {
|
||||
.open = qcrypto_block_qcow_open,
|
||||
.create = qcrypto_block_qcow_create,
|
||||
.cleanup = qcrypto_block_qcow_cleanup,
|
||||
.decrypt = qcrypto_block_qcow_decrypt,
|
||||
.encrypt = qcrypto_block_qcow_encrypt,
|
||||
.has_format = qcrypto_block_qcow_has_format,
|
||||
};
|
28
crypto/block-qcow.h
Normal file
28
crypto/block-qcow.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* QEMU Crypto block device encryption QCow/QCow2 AES-CBC format
|
||||
*
|
||||
* Copyright (c) 2015-2016 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QCRYPTO_BLOCK_QCOW_H__
|
||||
#define QCRYPTO_BLOCK_QCOW_H__
|
||||
|
||||
#include "crypto/blockpriv.h"
|
||||
|
||||
extern const QCryptoBlockDriver qcrypto_block_driver_qcow;
|
||||
|
||||
#endif /* QCRYPTO_BLOCK_QCOW_H__ */
|
258
crypto/block.c
Normal file
258
crypto/block.c
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* QEMU Crypto block device encryption
|
||||
*
|
||||
* Copyright (c) 2015-2016 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "crypto/blockpriv.h"
|
||||
#include "crypto/block-qcow.h"
|
||||
|
||||
static const QCryptoBlockDriver *qcrypto_block_drivers[] = {
|
||||
[Q_CRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow,
|
||||
};
|
||||
|
||||
|
||||
bool qcrypto_block_has_format(QCryptoBlockFormat format,
|
||||
const uint8_t *buf,
|
||||
size_t len)
|
||||
{
|
||||
const QCryptoBlockDriver *driver;
|
||||
|
||||
if (format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
|
||||
!qcrypto_block_drivers[format]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
driver = qcrypto_block_drivers[format];
|
||||
|
||||
return driver->has_format(buf, len);
|
||||
}
|
||||
|
||||
|
||||
QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options,
|
||||
QCryptoBlockReadFunc readfunc,
|
||||
void *opaque,
|
||||
unsigned int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoBlock *block = g_new0(QCryptoBlock, 1);
|
||||
|
||||
block->format = options->format;
|
||||
|
||||
if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
|
||||
!qcrypto_block_drivers[options->format]) {
|
||||
error_setg(errp, "Unsupported block driver %d", options->format);
|
||||
g_free(block);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
block->driver = qcrypto_block_drivers[options->format];
|
||||
|
||||
if (block->driver->open(block, options,
|
||||
readfunc, opaque, flags, errp) < 0) {
|
||||
g_free(block);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
|
||||
QCryptoBlockInitFunc initfunc,
|
||||
QCryptoBlockWriteFunc writefunc,
|
||||
void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoBlock *block = g_new0(QCryptoBlock, 1);
|
||||
|
||||
block->format = options->format;
|
||||
|
||||
if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
|
||||
!qcrypto_block_drivers[options->format]) {
|
||||
error_setg(errp, "Unsupported block driver %d", options->format);
|
||||
g_free(block);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
block->driver = qcrypto_block_drivers[options->format];
|
||||
|
||||
if (block->driver->create(block, options, initfunc,
|
||||
writefunc, opaque, errp) < 0) {
|
||||
g_free(block);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
int qcrypto_block_decrypt(QCryptoBlock *block,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
return block->driver->decrypt(block, startsector, buf, len, errp);
|
||||
}
|
||||
|
||||
|
||||
int qcrypto_block_encrypt(QCryptoBlock *block,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
return block->driver->encrypt(block, startsector, buf, len, errp);
|
||||
}
|
||||
|
||||
|
||||
QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block)
|
||||
{
|
||||
return block->cipher;
|
||||
}
|
||||
|
||||
|
||||
QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block)
|
||||
{
|
||||
return block->ivgen;
|
||||
}
|
||||
|
||||
|
||||
QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block)
|
||||
{
|
||||
return block->kdfhash;
|
||||
}
|
||||
|
||||
|
||||
uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block)
|
||||
{
|
||||
return block->payload_offset;
|
||||
}
|
||||
|
||||
|
||||
void qcrypto_block_free(QCryptoBlock *block)
|
||||
{
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
block->driver->cleanup(block);
|
||||
|
||||
qcrypto_cipher_free(block->cipher);
|
||||
qcrypto_ivgen_free(block->ivgen);
|
||||
g_free(block);
|
||||
}
|
||||
|
||||
|
||||
int qcrypto_block_decrypt_helper(QCryptoCipher *cipher,
|
||||
size_t niv,
|
||||
QCryptoIVGen *ivgen,
|
||||
int sectorsize,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
uint8_t *iv;
|
||||
int ret = -1;
|
||||
|
||||
iv = niv ? g_new0(uint8_t, niv) : NULL;
|
||||
|
||||
while (len > 0) {
|
||||
size_t nbytes;
|
||||
if (niv) {
|
||||
if (qcrypto_ivgen_calculate(ivgen,
|
||||
startsector,
|
||||
iv, niv,
|
||||
errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (qcrypto_cipher_setiv(cipher,
|
||||
iv, niv,
|
||||
errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
nbytes = len > sectorsize ? sectorsize : len;
|
||||
if (qcrypto_cipher_decrypt(cipher, buf, buf,
|
||||
nbytes, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
startsector++;
|
||||
buf += nbytes;
|
||||
len -= nbytes;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
g_free(iv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int qcrypto_block_encrypt_helper(QCryptoCipher *cipher,
|
||||
size_t niv,
|
||||
QCryptoIVGen *ivgen,
|
||||
int sectorsize,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
uint8_t *iv;
|
||||
int ret = -1;
|
||||
|
||||
iv = niv ? g_new0(uint8_t, niv) : NULL;
|
||||
|
||||
while (len > 0) {
|
||||
size_t nbytes;
|
||||
if (niv) {
|
||||
if (qcrypto_ivgen_calculate(ivgen,
|
||||
startsector,
|
||||
iv, niv,
|
||||
errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (qcrypto_cipher_setiv(cipher,
|
||||
iv, niv,
|
||||
errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
nbytes = len > sectorsize ? sectorsize : len;
|
||||
if (qcrypto_cipher_encrypt(cipher, buf, buf,
|
||||
nbytes, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
startsector++;
|
||||
buf += nbytes;
|
||||
len -= nbytes;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
g_free(iv);
|
||||
return ret;
|
||||
}
|
92
crypto/blockpriv.h
Normal file
92
crypto/blockpriv.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QEMU Crypto block device encryption
|
||||
*
|
||||
* Copyright (c) 2015-2016 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QCRYPTO_BLOCK_PRIV_H__
|
||||
#define QCRYPTO_BLOCK_PRIV_H__
|
||||
|
||||
#include "crypto/block.h"
|
||||
|
||||
typedef struct QCryptoBlockDriver QCryptoBlockDriver;
|
||||
|
||||
struct QCryptoBlock {
|
||||
QCryptoBlockFormat format;
|
||||
|
||||
const QCryptoBlockDriver *driver;
|
||||
void *opaque;
|
||||
|
||||
QCryptoCipher *cipher;
|
||||
QCryptoIVGen *ivgen;
|
||||
QCryptoHashAlgorithm kdfhash;
|
||||
size_t niv;
|
||||
uint64_t payload_offset; /* In bytes */
|
||||
};
|
||||
|
||||
struct QCryptoBlockDriver {
|
||||
int (*open)(QCryptoBlock *block,
|
||||
QCryptoBlockOpenOptions *options,
|
||||
QCryptoBlockReadFunc readfunc,
|
||||
void *opaque,
|
||||
unsigned int flags,
|
||||
Error **errp);
|
||||
|
||||
int (*create)(QCryptoBlock *block,
|
||||
QCryptoBlockCreateOptions *options,
|
||||
QCryptoBlockInitFunc initfunc,
|
||||
QCryptoBlockWriteFunc writefunc,
|
||||
void *opaque,
|
||||
Error **errp);
|
||||
|
||||
void (*cleanup)(QCryptoBlock *block);
|
||||
|
||||
int (*encrypt)(QCryptoBlock *block,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp);
|
||||
int (*decrypt)(QCryptoBlock *block,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp);
|
||||
|
||||
bool (*has_format)(const uint8_t *buf,
|
||||
size_t buflen);
|
||||
};
|
||||
|
||||
|
||||
int qcrypto_block_decrypt_helper(QCryptoCipher *cipher,
|
||||
size_t niv,
|
||||
QCryptoIVGen *ivgen,
|
||||
int sectorsize,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp);
|
||||
|
||||
int qcrypto_block_encrypt_helper(QCryptoCipher *cipher,
|
||||
size_t niv,
|
||||
QCryptoIVGen *ivgen,
|
||||
int sectorsize,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp);
|
||||
|
||||
#endif /* QCRYPTO_BLOCK_PRIV_H__ */
|
232
include/crypto/block.h
Normal file
232
include/crypto/block.h
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* QEMU Crypto block device encryption
|
||||
*
|
||||
* Copyright (c) 2015-2016 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QCRYPTO_BLOCK_H__
|
||||
#define QCRYPTO_BLOCK_H__
|
||||
|
||||
#include "crypto/cipher.h"
|
||||
#include "crypto/ivgen.h"
|
||||
|
||||
typedef struct QCryptoBlock QCryptoBlock;
|
||||
|
||||
/* See also QCryptoBlockFormat, QCryptoBlockCreateOptions
|
||||
* and QCryptoBlockOpenOptions in qapi/crypto.json */
|
||||
|
||||
typedef ssize_t (*QCryptoBlockReadFunc)(QCryptoBlock *block,
|
||||
size_t offset,
|
||||
uint8_t *buf,
|
||||
size_t buflen,
|
||||
Error **errp,
|
||||
void *opaque);
|
||||
|
||||
typedef ssize_t (*QCryptoBlockInitFunc)(QCryptoBlock *block,
|
||||
size_t headerlen,
|
||||
Error **errp,
|
||||
void *opaque);
|
||||
|
||||
typedef ssize_t (*QCryptoBlockWriteFunc)(QCryptoBlock *block,
|
||||
size_t offset,
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
Error **errp,
|
||||
void *opaque);
|
||||
|
||||
/**
|
||||
* qcrypto_block_has_format:
|
||||
* @format: the encryption format
|
||||
* @buf: the data from head of the volume
|
||||
* @len: the length of @buf in bytes
|
||||
*
|
||||
* Given @len bytes of data from the head of a storage volume
|
||||
* in @buf, probe to determine if the volume has the encryption
|
||||
* format specified in @format.
|
||||
*
|
||||
* Returns: true if the data in @buf matches @format
|
||||
*/
|
||||
bool qcrypto_block_has_format(QCryptoBlockFormat format,
|
||||
const uint8_t *buf,
|
||||
size_t buflen);
|
||||
|
||||
typedef enum {
|
||||
QCRYPTO_BLOCK_OPEN_NO_IO = (1 << 0),
|
||||
} QCryptoBlockOpenFlags;
|
||||
|
||||
/**
|
||||
* qcrypto_block_open:
|
||||
* @options: the encryption options
|
||||
* @readfunc: callback for reading data from the volume
|
||||
* @opaque: data to pass to @readfunc
|
||||
* @flags: bitmask of QCryptoBlockOpenFlags values
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Create a new block encryption object for an existing
|
||||
* storage volume encrypted with format identified by
|
||||
* the parameters in @options.
|
||||
*
|
||||
* This will use @readfunc to initialize the encryption
|
||||
* context based on the volume header(s), extracting the
|
||||
* master key(s) as required.
|
||||
*
|
||||
* If @flags contains QCRYPTO_BLOCK_OPEN_NO_IO then
|
||||
* the open process will be optimized to skip any parts
|
||||
* that are only required to perform I/O. In particular
|
||||
* this would usually avoid the need to decrypt any
|
||||
* master keys. The only thing that can be done with
|
||||
* the resulting QCryptoBlock object would be to query
|
||||
* metadata such as the payload offset. There will be
|
||||
* no cipher or ivgen objects available.
|
||||
*
|
||||
* If any part of initializing the encryption context
|
||||
* fails an error will be returned. This could be due
|
||||
* to the volume being in the wrong format, a cipher
|
||||
* or IV generator algorithm that is not supported,
|
||||
* or incorrect passphrases.
|
||||
*
|
||||
* Returns: a block encryption format, or NULL on error
|
||||
*/
|
||||
QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options,
|
||||
QCryptoBlockReadFunc readfunc,
|
||||
void *opaque,
|
||||
unsigned int flags,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* qcrypto_block_create:
|
||||
* @format: the encryption format
|
||||
* @initfunc: callback for initializing volume header
|
||||
* @writefunc: callback for writing data to the volume header
|
||||
* @opaque: data to pass to @initfunc and @writefunc
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Create a new block encryption object for initializing
|
||||
* a storage volume to be encrypted with format identified
|
||||
* by the parameters in @options.
|
||||
*
|
||||
* This method will allocate space for a new volume header
|
||||
* using @initfunc and then write header data using @writefunc,
|
||||
* generating new master keys, etc as required. Any existing
|
||||
* data present on the volume will be irrevocably destroyed.
|
||||
*
|
||||
* If any part of initializing the encryption context
|
||||
* fails an error will be returned. This could be due
|
||||
* to the volume being in the wrong format, a cipher
|
||||
* or IV generator algorithm that is not supported,
|
||||
* or incorrect passphrases.
|
||||
*
|
||||
* Returns: a block encryption format, or NULL on error
|
||||
*/
|
||||
QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
|
||||
QCryptoBlockInitFunc initfunc,
|
||||
QCryptoBlockWriteFunc writefunc,
|
||||
void *opaque,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* @qcrypto_block_decrypt:
|
||||
* @block: the block encryption object
|
||||
* @startsector: the sector from which @buf was read
|
||||
* @buf: the buffer to decrypt
|
||||
* @len: the length of @buf in bytes
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Decrypt @len bytes of cipher text in @buf, writing
|
||||
* plain text back into @buf
|
||||
*
|
||||
* Returns 0 on success, -1 on failure
|
||||
*/
|
||||
int qcrypto_block_decrypt(QCryptoBlock *block,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* @qcrypto_block_encrypt:
|
||||
* @block: the block encryption object
|
||||
* @startsector: the sector to which @buf will be written
|
||||
* @buf: the buffer to decrypt
|
||||
* @len: the length of @buf in bytes
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Encrypt @len bytes of plain text in @buf, writing
|
||||
* cipher text back into @buf
|
||||
*
|
||||
* Returns 0 on success, -1 on failure
|
||||
*/
|
||||
int qcrypto_block_encrypt(QCryptoBlock *block,
|
||||
uint64_t startsector,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* qcrypto_block_get_cipher:
|
||||
* @block: the block encryption object
|
||||
*
|
||||
* Get the cipher to use for payload encryption
|
||||
*
|
||||
* Returns: the cipher object
|
||||
*/
|
||||
QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block);
|
||||
|
||||
/**
|
||||
* qcrypto_block_get_ivgen:
|
||||
* @block: the block encryption object
|
||||
*
|
||||
* Get the initialization vector generator to use for
|
||||
* payload encryption
|
||||
*
|
||||
* Returns: the IV generator object
|
||||
*/
|
||||
QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block);
|
||||
|
||||
|
||||
/**
|
||||
* qcrypto_block_get_kdf_hash:
|
||||
* @block: the block encryption object
|
||||
*
|
||||
* Get the hash algorithm used with the key derivation
|
||||
* function
|
||||
*
|
||||
* Returns: the hash algorithm
|
||||
*/
|
||||
QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block);
|
||||
|
||||
/**
|
||||
* qcrypto_block_get_payload_offset:
|
||||
* @block: the block encryption object
|
||||
*
|
||||
* Get the offset to the payload indicated by the
|
||||
* encryption header, in bytes.
|
||||
*
|
||||
* Returns: the payload offset in bytes
|
||||
*/
|
||||
uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block);
|
||||
|
||||
/**
|
||||
* qcrypto_block_free:
|
||||
* @block: the block encryption object
|
||||
*
|
||||
* Release all resources associated with the encryption
|
||||
* object
|
||||
*/
|
||||
void qcrypto_block_free(QCryptoBlock *block);
|
||||
|
||||
#endif /* QCRYPTO_BLOCK_H__ */
|
@ -109,3 +109,71 @@
|
||||
{ 'enum': 'QCryptoIVGenAlgorithm',
|
||||
'prefix': 'QCRYPTO_IVGEN_ALG',
|
||||
'data': ['plain', 'plain64', 'essiv']}
|
||||
|
||||
##
|
||||
# QCryptoBlockFormat:
|
||||
#
|
||||
# The supported full disk encryption formats
|
||||
#
|
||||
# @qcow: QCow/QCow2 built-in AES-CBC encryption. Use only
|
||||
# for liberating data from old images.
|
||||
#
|
||||
# Since: 2.6
|
||||
##
|
||||
{ 'enum': 'QCryptoBlockFormat',
|
||||
# 'prefix': 'QCRYPTO_BLOCK_FORMAT',
|
||||
'data': ['qcow']}
|
||||
|
||||
##
|
||||
# QCryptoBlockOptionsBase:
|
||||
#
|
||||
# The common options that apply to all full disk
|
||||
# encryption formats
|
||||
#
|
||||
# @format: the encryption format
|
||||
#
|
||||
# Since: 2.6
|
||||
##
|
||||
{ 'struct': 'QCryptoBlockOptionsBase',
|
||||
'data': { 'format': 'QCryptoBlockFormat' }}
|
||||
|
||||
##
|
||||
# QCryptoBlockOptionsQCow:
|
||||
#
|
||||
# The options that apply to QCow/QCow2 AES-CBC encryption format
|
||||
#
|
||||
# @key-secret: #optional the ID of a QCryptoSecret object providing the
|
||||
# decryption key. Mandatory except when probing image for
|
||||
# metadata only.
|
||||
#
|
||||
# Since: 2.6
|
||||
##
|
||||
{ 'struct': 'QCryptoBlockOptionsQCow',
|
||||
'data': { '*key-secret': 'str' }}
|
||||
|
||||
##
|
||||
# QCryptoBlockOpenOptions:
|
||||
#
|
||||
# The options that are available for all encryption formats
|
||||
# when opening an existing volume
|
||||
#
|
||||
# Since: 2.6
|
||||
##
|
||||
{ 'union': 'QCryptoBlockOpenOptions',
|
||||
'base': 'QCryptoBlockOptionsBase',
|
||||
'discriminator': 'format',
|
||||
'data': { 'qcow': 'QCryptoBlockOptionsQCow' } }
|
||||
|
||||
|
||||
##
|
||||
# QCryptoBlockCreateOptions:
|
||||
#
|
||||
# The options that are available for all encryption formats
|
||||
# when initializing a new volume
|
||||
#
|
||||
# Since: 2.6
|
||||
##
|
||||
{ 'union': 'QCryptoBlockCreateOptions',
|
||||
'base': 'QCryptoBlockOptionsBase',
|
||||
'discriminator': 'format',
|
||||
'data': { 'qcow': 'QCryptoBlockOptionsQCow' } }
|
||||
|
1
tests/.gitignore
vendored
1
tests/.gitignore
vendored
@ -13,6 +13,7 @@ test-bitops
|
||||
test-blockjob-txn
|
||||
test-coroutine
|
||||
test-crypto-afsplit
|
||||
test-crypto-block
|
||||
test-crypto-cipher
|
||||
test-crypto-hash
|
||||
test-crypto-ivgen
|
||||
|
@ -96,6 +96,7 @@ check-unit-$(if $(CONFIG_NETTLE),y,$(CONFIG_GCRYPT_KDF)) += tests/test-crypto-pb
|
||||
check-unit-y += tests/test-crypto-ivgen$(EXESUF)
|
||||
check-unit-y += tests/test-crypto-afsplit$(EXESUF)
|
||||
check-unit-y += tests/test-crypto-xts$(EXESUF)
|
||||
check-unit-y += tests/test-crypto-block$(EXESUF)
|
||||
|
||||
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
|
||||
|
||||
@ -504,6 +505,7 @@ tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \
|
||||
tests/test-crypto-pbkdf$(EXESUF): tests/test-crypto-pbkdf.o $(test-crypto-obj-y)
|
||||
tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y)
|
||||
tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y)
|
||||
tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y)
|
||||
|
||||
libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
|
||||
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
|
||||
|
239
tests/test-crypto-block.c
Normal file
239
tests/test-crypto-block.c
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* QEMU Crypto block encryption
|
||||
*
|
||||
* Copyright (c) 2016 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "crypto/init.h"
|
||||
#include "crypto/block.h"
|
||||
#include "qemu/buffer.h"
|
||||
#include "crypto/secret.h"
|
||||
|
||||
static QCryptoBlockCreateOptions qcow_create_opts = {
|
||||
.format = Q_CRYPTO_BLOCK_FORMAT_QCOW,
|
||||
.u.qcow = {
|
||||
.has_key_secret = true,
|
||||
.key_secret = (char *)"sec0",
|
||||
},
|
||||
};
|
||||
|
||||
static QCryptoBlockOpenOptions qcow_open_opts = {
|
||||
.format = Q_CRYPTO_BLOCK_FORMAT_QCOW,
|
||||
.u.qcow = {
|
||||
.has_key_secret = true,
|
||||
.key_secret = (char *)"sec0",
|
||||
},
|
||||
};
|
||||
|
||||
static struct QCryptoBlockTestData {
|
||||
const char *path;
|
||||
QCryptoBlockCreateOptions *create_opts;
|
||||
QCryptoBlockOpenOptions *open_opts;
|
||||
|
||||
bool expect_header;
|
||||
|
||||
QCryptoCipherAlgorithm cipher_alg;
|
||||
QCryptoCipherMode cipher_mode;
|
||||
QCryptoHashAlgorithm hash_alg;
|
||||
|
||||
QCryptoIVGenAlgorithm ivgen_alg;
|
||||
QCryptoCipherAlgorithm ivgen_hash;
|
||||
} test_data[] = {
|
||||
{
|
||||
.path = "/crypto/block/qcow",
|
||||
.create_opts = &qcow_create_opts,
|
||||
.open_opts = &qcow_open_opts,
|
||||
|
||||
.expect_header = false,
|
||||
|
||||
.cipher_alg = QCRYPTO_CIPHER_ALG_AES_128,
|
||||
.cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
|
||||
|
||||
.ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static ssize_t test_block_read_func(QCryptoBlock *block,
|
||||
size_t offset,
|
||||
uint8_t *buf,
|
||||
size_t buflen,
|
||||
Error **errp,
|
||||
void *opaque)
|
||||
{
|
||||
Buffer *header = opaque;
|
||||
|
||||
g_assert_cmpint(offset + buflen, <=, header->capacity);
|
||||
|
||||
memcpy(buf, header->buffer + offset, buflen);
|
||||
|
||||
return buflen;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t test_block_init_func(QCryptoBlock *block,
|
||||
size_t headerlen,
|
||||
Error **errp,
|
||||
void *opaque)
|
||||
{
|
||||
Buffer *header = opaque;
|
||||
|
||||
g_assert_cmpint(header->capacity, ==, 0);
|
||||
|
||||
buffer_reserve(header, headerlen);
|
||||
|
||||
return headerlen;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t test_block_write_func(QCryptoBlock *block,
|
||||
size_t offset,
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
Error **errp,
|
||||
void *opaque)
|
||||
{
|
||||
Buffer *header = opaque;
|
||||
|
||||
g_assert_cmpint(buflen + offset, <=, header->capacity);
|
||||
|
||||
memcpy(header->buffer + offset, buf, buflen);
|
||||
header->offset = offset + buflen;
|
||||
|
||||
return buflen;
|
||||
}
|
||||
|
||||
|
||||
static Object *test_block_secret(void)
|
||||
{
|
||||
return object_new_with_props(
|
||||
TYPE_QCRYPTO_SECRET,
|
||||
object_get_objects_root(),
|
||||
"sec0",
|
||||
&error_abort,
|
||||
"data", "123456",
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void test_block_assert_setup(const struct QCryptoBlockTestData *data,
|
||||
QCryptoBlock *blk)
|
||||
{
|
||||
QCryptoIVGen *ivgen;
|
||||
QCryptoCipher *cipher;
|
||||
|
||||
ivgen = qcrypto_block_get_ivgen(blk);
|
||||
cipher = qcrypto_block_get_cipher(blk);
|
||||
|
||||
g_assert(ivgen);
|
||||
g_assert(cipher);
|
||||
|
||||
g_assert_cmpint(data->cipher_alg, ==, cipher->alg);
|
||||
g_assert_cmpint(data->cipher_mode, ==, cipher->mode);
|
||||
g_assert_cmpint(data->hash_alg, ==,
|
||||
qcrypto_block_get_kdf_hash(blk));
|
||||
|
||||
g_assert_cmpint(data->ivgen_alg, ==,
|
||||
qcrypto_ivgen_get_algorithm(ivgen));
|
||||
g_assert_cmpint(data->ivgen_hash, ==,
|
||||
qcrypto_ivgen_get_hash(ivgen));
|
||||
}
|
||||
|
||||
|
||||
static void test_block(gconstpointer opaque)
|
||||
{
|
||||
const struct QCryptoBlockTestData *data = opaque;
|
||||
QCryptoBlock *blk;
|
||||
Buffer header;
|
||||
Object *sec = test_block_secret();
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
buffer_init(&header, "header");
|
||||
|
||||
blk = qcrypto_block_create(data->create_opts,
|
||||
test_block_init_func,
|
||||
test_block_write_func,
|
||||
&header,
|
||||
&error_abort);
|
||||
g_assert(blk);
|
||||
|
||||
if (data->expect_header) {
|
||||
g_assert_cmpint(header.capacity, >, 0);
|
||||
} else {
|
||||
g_assert_cmpint(header.capacity, ==, 0);
|
||||
}
|
||||
|
||||
test_block_assert_setup(data, blk);
|
||||
|
||||
qcrypto_block_free(blk);
|
||||
object_unparent(sec);
|
||||
|
||||
/* Ensure we can't open without the secret */
|
||||
blk = qcrypto_block_open(data->open_opts,
|
||||
test_block_read_func,
|
||||
&header,
|
||||
0,
|
||||
NULL);
|
||||
g_assert(blk == NULL);
|
||||
|
||||
/* Ensure we can't open without the secret, unless NO_IO */
|
||||
blk = qcrypto_block_open(data->open_opts,
|
||||
test_block_read_func,
|
||||
&header,
|
||||
QCRYPTO_BLOCK_OPEN_NO_IO,
|
||||
&error_abort);
|
||||
|
||||
g_assert(qcrypto_block_get_cipher(blk) == NULL);
|
||||
g_assert(qcrypto_block_get_ivgen(blk) == NULL);
|
||||
|
||||
qcrypto_block_free(blk);
|
||||
|
||||
|
||||
/* Now open for real with secret */
|
||||
sec = test_block_secret();
|
||||
blk = qcrypto_block_open(data->open_opts,
|
||||
test_block_read_func,
|
||||
&header,
|
||||
0,
|
||||
&error_abort);
|
||||
g_assert(blk);
|
||||
|
||||
test_block_assert_setup(data, blk);
|
||||
|
||||
qcrypto_block_free(blk);
|
||||
|
||||
object_unparent(sec);
|
||||
|
||||
buffer_free(&header);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
g_assert(qcrypto_init(NULL) == 0);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
|
||||
g_test_add_data_func(test_data[i].path, &test_data[i], test_block);
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
}
|
Loading…
Reference in New Issue
Block a user