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 += ivgen-plain64.o
|
||||||
crypto-obj-y += afsplit.o
|
crypto-obj-y += afsplit.o
|
||||||
crypto-obj-y += xts.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
|
# Let the userspace emulators avoid linking gnutls/etc
|
||||||
crypto-aes-obj-y = aes.o
|
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',
|
{ 'enum': 'QCryptoIVGenAlgorithm',
|
||||||
'prefix': 'QCRYPTO_IVGEN_ALG',
|
'prefix': 'QCRYPTO_IVGEN_ALG',
|
||||||
'data': ['plain', 'plain64', 'essiv']}
|
'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-blockjob-txn
|
||||||
test-coroutine
|
test-coroutine
|
||||||
test-crypto-afsplit
|
test-crypto-afsplit
|
||||||
|
test-crypto-block
|
||||||
test-crypto-cipher
|
test-crypto-cipher
|
||||||
test-crypto-hash
|
test-crypto-hash
|
||||||
test-crypto-ivgen
|
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-ivgen$(EXESUF)
|
||||||
check-unit-y += tests/test-crypto-afsplit$(EXESUF)
|
check-unit-y += tests/test-crypto-afsplit$(EXESUF)
|
||||||
check-unit-y += tests/test-crypto-xts$(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
|
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-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-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-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/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
|
||||||
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.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