block: Support detached LUKS header creation using qemu-img

Even though a LUKS header might be created with cryptsetup,
qemu-img should be enhanced to accommodate it as well.

Add the 'detached-header' option to specify the creation of
a detached LUKS header. This is how it is used:
$ qemu-img create --object secret,id=sec0,data=abc123 -f luks
> -o cipher-alg=aes-256,cipher-mode=xts -o key-secret=sec0
> -o detached-header=true header.luks

Using qemu-img or cryptsetup tools to query information of
an LUKS header image as follows:

Assume a detached LUKS header image has been created by:
$ dd if=/dev/zero of=test-header.img bs=1M count=32
$ dd if=/dev/zero of=test-payload.img bs=1M count=1000
$ cryptsetup luksFormat --header test-header.img test-payload.img
> --force-password --type luks1

Header image information could be queried using cryptsetup:
$ cryptsetup luksDump test-header.img

or qemu-img:
$ qemu-img info 'json:{"driver":"luks","file":{"filename":
> "test-payload.img"},"header":{"filename":"test-header.img"}}'

When using qemu-img, keep in mind that the entire disk
information specified by the JSON-format string above must be
supplied on the commandline; if not, an overlay check will reveal
a problem with the LUKS volume check logic.

Signed-off-by: Hyman Huang <yong.huang@smartx.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
[changed to pass 'cflags' to block_crypto_co_create_generic]
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Hyman Huang 2024-01-30 13:37:23 +08:00 committed by Daniel P. Berrangé
parent d0112eb415
commit 35286daeca
4 changed files with 26 additions and 4 deletions

View File

@ -7357,7 +7357,10 @@ void bdrv_img_create(const char *filename, const char *fmt,
goto out; goto out;
} }
if (size == -1) { /* Parameter 'size' is not needed for detached LUKS header */
if (size == -1 &&
!(!strcmp(fmt, "luks") &&
qemu_opt_get_bool(opts, "detached-header", false))) {
error_setg(errp, "Image creation needs a size parameter"); error_setg(errp, "Image creation needs a size parameter");
goto out; goto out;
} }

View File

@ -231,6 +231,7 @@ static QemuOptsList block_crypto_create_opts_luks = {
BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG(""), BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG(""),
BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG(""), BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG(""),
BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""), BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""),
BLOCK_CRYPTO_OPT_DEF_LUKS_DETACHED_HEADER(""),
{ /* end of list */ } { /* end of list */ }
}, },
}; };
@ -405,7 +406,7 @@ block_crypto_co_create_generic(BlockDriverState *bs, int64_t size,
data = (struct BlockCryptoCreateData) { data = (struct BlockCryptoCreateData) {
.blk = blk, .blk = blk,
.size = size, .size = flags & QCRYPTO_BLOCK_CREATE_DETACHED ? 0 : size,
.prealloc = prealloc, .prealloc = prealloc,
}; };
@ -791,6 +792,9 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
PreallocMode prealloc; PreallocMode prealloc;
char *buf = NULL; char *buf = NULL;
int64_t size; int64_t size;
bool detached_hdr =
qemu_opt_get_bool(opts, "detached-header", false);
unsigned int cflags = 0;
int ret; int ret;
Error *local_err = NULL; Error *local_err = NULL;
@ -830,9 +834,13 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
goto fail; goto fail;
} }
if (detached_hdr) {
cflags |= QCRYPTO_BLOCK_CREATE_DETACHED;
}
/* Create format layer */ /* Create format layer */
ret = block_crypto_co_create_generic(bs, size, create_opts, ret = block_crypto_co_create_generic(bs, size, create_opts,
prealloc, 0, errp); prealloc, cflags, errp);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }

View File

@ -41,6 +41,7 @@
#define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg" #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg"
#define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg" #define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg"
#define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time" #define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time"
#define BLOCK_CRYPTO_OPT_LUKS_DETACHED_HEADER "detached-header"
#define BLOCK_CRYPTO_OPT_LUKS_KEYSLOT "keyslot" #define BLOCK_CRYPTO_OPT_LUKS_KEYSLOT "keyslot"
#define BLOCK_CRYPTO_OPT_LUKS_STATE "state" #define BLOCK_CRYPTO_OPT_LUKS_STATE "state"
#define BLOCK_CRYPTO_OPT_LUKS_OLD_SECRET "old-secret" #define BLOCK_CRYPTO_OPT_LUKS_OLD_SECRET "old-secret"
@ -100,6 +101,13 @@
.help = "Select new state of affected keyslots (active/inactive)",\ .help = "Select new state of affected keyslots (active/inactive)",\
} }
#define BLOCK_CRYPTO_OPT_DEF_LUKS_DETACHED_HEADER(prefix) \
{ \
.name = prefix BLOCK_CRYPTO_OPT_LUKS_DETACHED_HEADER, \
.type = QEMU_OPT_BOOL, \
.help = "Create a detached LUKS header", \
}
#define BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT(prefix) \ #define BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT(prefix) \
{ \ { \
.name = prefix BLOCK_CRYPTO_OPT_LUKS_KEYSLOT, \ .name = prefix BLOCK_CRYPTO_OPT_LUKS_KEYSLOT, \

View File

@ -226,6 +226,8 @@
# @iter-time: number of milliseconds to spend in PBKDF passphrase # @iter-time: number of milliseconds to spend in PBKDF passphrase
# processing. Currently defaults to 2000. (since 2.8) # processing. Currently defaults to 2000. (since 2.8)
# #
# @detached-header: create a detached LUKS header. (since 9.0)
#
# Since: 2.6 # Since: 2.6
## ##
{ 'struct': 'QCryptoBlockCreateOptionsLUKS', { 'struct': 'QCryptoBlockCreateOptionsLUKS',
@ -235,7 +237,8 @@
'*ivgen-alg': 'QCryptoIVGenAlgorithm', '*ivgen-alg': 'QCryptoIVGenAlgorithm',
'*ivgen-hash-alg': 'QCryptoHashAlgorithm', '*ivgen-hash-alg': 'QCryptoHashAlgorithm',
'*hash-alg': 'QCryptoHashAlgorithm', '*hash-alg': 'QCryptoHashAlgorithm',
'*iter-time': 'int'}} '*iter-time': 'int',
'*detached-header': 'bool'}}
## ##
# @QCryptoBlockOpenOptions: # @QCryptoBlockOpenOptions: