Refactor LUKS2 metadata parameters calculations.

Move all metadata size and data offset calculations
logic away from LUKS2_generate_hdr. The function
was meant to generate solely LUKS2 header on disk json
format.

The aim is to have all logic related data offset and metadata
size in one place available to be calculated in advance so
that we can easily extend the code.
This commit is contained in:
Ondrej Kozina
2023-05-10 11:44:58 +02:00
parent 11d8c58c72
commit 926679f7f1
4 changed files with 124 additions and 71 deletions

View File

@@ -155,6 +155,10 @@ int create_or_reload_device_with_integrity(struct crypt_device *cd, const char *
struct device *crypt_metadata_device(struct crypt_device *cd); struct device *crypt_metadata_device(struct crypt_device *cd);
struct device *crypt_data_device(struct crypt_device *cd); struct device *crypt_data_device(struct crypt_device *cd);
uint64_t crypt_get_metadata_size_bytes(struct crypt_device *cd);
uint64_t crypt_get_keyslots_size_bytes(struct crypt_device *cd);
uint64_t crypt_get_data_offset_sectors(struct crypt_device *cd);
int crypt_confirm(struct crypt_device *cd, const char *msg); int crypt_confirm(struct crypt_device *cd, const char *msg);
char *crypt_lookup_dev(const char *dev_id); char *crypt_lookup_dev(const char *dev_id);

View File

@@ -384,10 +384,15 @@ int LUKS2_generate_hdr(
const char *uuid, const char *uuid,
unsigned int sector_size, unsigned int sector_size,
uint64_t data_offset, uint64_t data_offset,
uint64_t align_offset, uint64_t metadata_size_bytes,
uint64_t required_alignment, uint64_t keyslots_size_bytes);
uint64_t metadata_size,
uint64_t keyslots_size); int LUKS2_hdr_get_storage_params(struct crypt_device *cd,
uint64_t alignment_offset_bytes,
uint64_t alignment_bytes,
uint64_t *ret_metadata_size_bytes,
uint64_t *ret_keyslots_size_bytes,
uint64_t *ret_data_offset_bytes);
int LUKS2_check_metadata_area_size(uint64_t metadata_size); int LUKS2_check_metadata_area_size(uint64_t metadata_size);
int LUKS2_check_keyslots_area_size(uint64_t keyslots_size); int LUKS2_check_keyslots_area_size(uint64_t keyslots_size);

View File

@@ -210,70 +210,24 @@ int LUKS2_generate_hdr(
const char *uuid, const char *uuid,
unsigned int sector_size, /* in bytes */ unsigned int sector_size, /* in bytes */
uint64_t data_offset, /* in bytes */ uint64_t data_offset, /* in bytes */
uint64_t align_offset, /* in bytes */ uint64_t metadata_size_bytes,
uint64_t required_alignment, uint64_t keyslots_size_bytes)
uint64_t metadata_size,
uint64_t keyslots_size)
{ {
struct json_object *jobj_segment, *jobj_integrity, *jobj_keyslots, *jobj_segments, *jobj_config; struct json_object *jobj_segment, *jobj_integrity, *jobj_keyslots, *jobj_segments, *jobj_config;
char cipher[128]; char cipher[128];
uuid_t partitionUuid; uuid_t partitionUuid;
int r, digest; int r, digest;
uint64_t mdev_size;
if (!metadata_size) hdr->hdr_size = metadata_size_bytes;
metadata_size = LUKS2_HDR_16K_LEN;
hdr->hdr_size = metadata_size;
if (data_offset && data_offset < get_min_offset(hdr)) {
log_err(cd, _("Requested data offset is too small."));
return -EINVAL;
}
/* Increase keyslot size according to data offset */
if (!keyslots_size && data_offset)
keyslots_size = data_offset - get_min_offset(hdr);
/* keyslots size has to be 4 KiB aligned */
keyslots_size -= (keyslots_size % 4096);
if (keyslots_size > LUKS2_MAX_KEYSLOTS_SIZE)
keyslots_size = LUKS2_MAX_KEYSLOTS_SIZE;
if (!keyslots_size) {
assert(LUKS2_DEFAULT_HDR_SIZE > 2 * LUKS2_HDR_OFFSET_MAX);
keyslots_size = LUKS2_DEFAULT_HDR_SIZE - get_min_offset(hdr);
/* Decrease keyslots_size due to metadata device being too small */
if (!device_size(crypt_metadata_device(cd), &mdev_size) &&
((keyslots_size + get_min_offset(hdr)) > mdev_size) &&
device_fallocate(crypt_metadata_device(cd), keyslots_size + get_min_offset(hdr)) &&
(get_min_offset(hdr) <= mdev_size))
keyslots_size = mdev_size - get_min_offset(hdr);
}
/* Decrease keyslots_size if we have smaller data_offset */
if (data_offset && (keyslots_size + get_min_offset(hdr)) > data_offset) {
keyslots_size = data_offset - get_min_offset(hdr);
log_dbg(cd, "Decreasing keyslot area size to %" PRIu64
" bytes due to the requested data offset %"
PRIu64 " bytes.", keyslots_size, data_offset);
}
/* Data offset has priority */
if (!data_offset && required_alignment) {
data_offset = size_round_up(get_min_offset(hdr) + keyslots_size,
(size_t)required_alignment);
data_offset += align_offset;
}
log_dbg(cd, "Formatting LUKS2 with JSON metadata area %" PRIu64 log_dbg(cd, "Formatting LUKS2 with JSON metadata area %" PRIu64
" bytes and keyslots area %" PRIu64 " bytes.", " bytes and keyslots area %" PRIu64 " bytes.",
metadata_size - LUKS2_HDR_BIN_LEN, keyslots_size); metadata_size_bytes - LUKS2_HDR_BIN_LEN, keyslots_size_bytes);
if (keyslots_size < (LUKS2_HDR_OFFSET_MAX - 2*LUKS2_HDR_16K_LEN)) if (keyslots_size_bytes < (LUKS2_HDR_OFFSET_MAX - 2*LUKS2_HDR_16K_LEN))
log_std(cd, _("WARNING: keyslots area (%" PRIu64 " bytes) is very small," log_std(cd, _("WARNING: keyslots area (%" PRIu64 " bytes) is very small,"
" available LUKS2 keyslot count is very limited.\n"), " available LUKS2 keyslot count is very limited.\n"),
keyslots_size); keyslots_size_bytes);
hdr->seqid = 1; hdr->seqid = 1;
hdr->version = 2; hdr->version = 2;
@@ -364,8 +318,8 @@ int LUKS2_generate_hdr(
goto err; goto err;
} }
json_object_object_add(jobj_config, "json_size", crypt_jobj_new_uint64(metadata_size - LUKS2_HDR_BIN_LEN)); json_object_object_add(jobj_config, "json_size", crypt_jobj_new_uint64(metadata_size_bytes - LUKS2_HDR_BIN_LEN));
json_object_object_add(jobj_config, "keyslots_size", crypt_jobj_new_uint64(keyslots_size)); json_object_object_add(jobj_config, "keyslots_size", crypt_jobj_new_uint64(keyslots_size_bytes));
JSON_DBG(cd, hdr->jobj, "Header JSON:"); JSON_DBG(cd, hdr->jobj, "Header JSON:");
return 0; return 0;
@@ -443,3 +397,80 @@ int LUKS2_set_keyslots_size(struct luks2_hdr *hdr, uint64_t data_offset)
json_object_object_add(jobj_config, "keyslots_size", crypt_jobj_new_uint64(keyslots_size)); json_object_object_add(jobj_config, "keyslots_size", crypt_jobj_new_uint64(keyslots_size));
return 0; return 0;
} }
int LUKS2_hdr_get_storage_params(struct crypt_device *cd,
uint64_t alignment_offset_bytes,
uint64_t alignment_bytes,
uint64_t *ret_metadata_size_bytes,
uint64_t *ret_keyslots_size_bytes,
uint64_t *ret_data_offset_bytes)
{
uint64_t data_offset_bytes, keyslots_size_bytes, metadata_size_bytes, mdev_size_bytes;
assert(cd);
assert(ret_metadata_size_bytes);
assert(ret_keyslots_size_bytes);
assert(ret_data_offset_bytes);
metadata_size_bytes = crypt_get_metadata_size_bytes(cd);
keyslots_size_bytes = crypt_get_keyslots_size_bytes(cd);
data_offset_bytes = crypt_get_data_offset_sectors(cd) * SECTOR_SIZE;
if (!metadata_size_bytes)
metadata_size_bytes = LUKS2_HDR_16K_LEN;
if (data_offset_bytes && data_offset_bytes < 2 * metadata_size_bytes) {
log_err(cd, _("Requested data offset is too small."));
return -EINVAL;
}
/* Increase keyslot size according to data offset */
if (!keyslots_size_bytes && data_offset_bytes)
keyslots_size_bytes = data_offset_bytes - 2 * metadata_size_bytes;
/* keyslots size has to be 4 KiB aligned */
keyslots_size_bytes -= (keyslots_size_bytes % 4096);
if (keyslots_size_bytes > LUKS2_MAX_KEYSLOTS_SIZE)
keyslots_size_bytes = LUKS2_MAX_KEYSLOTS_SIZE;
if (!keyslots_size_bytes) {
assert(LUKS2_DEFAULT_HDR_SIZE > 2 * LUKS2_HDR_OFFSET_MAX);
keyslots_size_bytes = LUKS2_DEFAULT_HDR_SIZE - 2 * metadata_size_bytes;
/* Decrease keyslots_size due to metadata device being too small */
if (!device_size(crypt_metadata_device(cd), &mdev_size_bytes) &&
((keyslots_size_bytes + 2 * metadata_size_bytes) > mdev_size_bytes) &&
device_fallocate(crypt_metadata_device(cd), keyslots_size_bytes + 2 * metadata_size_bytes) &&
((2 * metadata_size_bytes) <= mdev_size_bytes))
keyslots_size_bytes = mdev_size_bytes - 2 * metadata_size_bytes;
}
/* Decrease keyslots_size if we have smaller data_offset */
if (data_offset_bytes && (keyslots_size_bytes + 2 * metadata_size_bytes) > data_offset_bytes) {
keyslots_size_bytes = data_offset_bytes - 2 * metadata_size_bytes;
log_dbg(cd, "Decreasing keyslot area size to %" PRIu64
" bytes due to the requested data offset %"
PRIu64 " bytes.", keyslots_size_bytes, data_offset_bytes);
}
/* Data offset has priority */
if (!data_offset_bytes && alignment_bytes) {
data_offset_bytes = size_round_up(2 * metadata_size_bytes + keyslots_size_bytes,
(size_t)alignment_bytes);
data_offset_bytes += alignment_offset_bytes;
}
if (crypt_get_metadata_size_bytes(cd) && (crypt_get_metadata_size_bytes(cd) != metadata_size_bytes))
log_std(cd, _("WARNING: LUKS2 metadata size changed to %" PRIu64 " bytes.\n"),
metadata_size_bytes);
if (crypt_get_keyslots_size_bytes(cd) && (crypt_get_keyslots_size_bytes(cd) != keyslots_size_bytes))
log_std(cd, _("WARNING: LUKS2 keyslots area size changed to %" PRIu64 " bytes.\n"),
keyslots_size_bytes);
*ret_metadata_size_bytes = metadata_size_bytes;
*ret_keyslots_size_bytes = keyslots_size_bytes;
*ret_data_offset_bytes = data_offset_bytes;
return 0;
}

View File

@@ -223,6 +223,24 @@ struct device *crypt_data_device(struct crypt_device *cd)
return cd->device; return cd->device;
} }
uint64_t crypt_get_metadata_size_bytes(struct crypt_device *cd)
{
assert(cd);
return cd->metadata_size;
}
uint64_t crypt_get_keyslots_size_bytes(struct crypt_device *cd)
{
assert(cd);
return cd->keyslots_size;
}
uint64_t crypt_get_data_offset_sectors(struct crypt_device *cd)
{
assert(cd);
return cd->data_offset;
}
int init_crypto(struct crypt_device *ctx) int init_crypto(struct crypt_device *ctx)
{ {
struct utsname uts; struct utsname uts;
@@ -1805,7 +1823,7 @@ static int _crypt_format_luks2(struct crypt_device *cd,
unsigned long alignment_offset = 0; unsigned long alignment_offset = 0;
unsigned int sector_size; unsigned int sector_size;
const char *integrity = params ? params->integrity : NULL; const char *integrity = params ? params->integrity : NULL;
uint64_t dev_size; uint64_t data_offset_bytes, dev_size, metadata_size_bytes, keyslots_size_bytes;
uint32_t dmc_flags; uint32_t dmc_flags;
cd->u.luks2.hdr.jobj = NULL; cd->u.luks2.hdr.jobj = NULL;
@@ -1956,25 +1974,20 @@ static int _crypt_format_luks2(struct crypt_device *cd,
goto out; goto out;
} }
r = LUKS2_hdr_get_storage_params(cd, alignment_offset, required_alignment,
&metadata_size_bytes, &keyslots_size_bytes, &data_offset_bytes);
if (r < 0)
goto out;
r = LUKS2_generate_hdr(cd, &cd->u.luks2.hdr, cd->volume_key, r = LUKS2_generate_hdr(cd, &cd->u.luks2.hdr, cd->volume_key,
cipher, cipher_mode, cipher, cipher_mode,
integrity, uuid, integrity, uuid,
sector_size, sector_size,
cd->data_offset * SECTOR_SIZE, data_offset_bytes,
alignment_offset, metadata_size_bytes, keyslots_size_bytes);
required_alignment,
cd->metadata_size, cd->keyslots_size);
if (r < 0) if (r < 0)
goto out; goto out;
if (cd->metadata_size && (cd->metadata_size != LUKS2_metadata_size(&cd->u.luks2.hdr)))
log_std(cd, _("WARNING: LUKS2 metadata size changed to %" PRIu64 " bytes.\n"),
LUKS2_metadata_size(&cd->u.luks2.hdr));
if (cd->keyslots_size && (cd->keyslots_size != LUKS2_keyslots_size(&cd->u.luks2.hdr)))
log_std(cd, _("WARNING: LUKS2 keyslots area size changed to %" PRIu64 " bytes.\n"),
LUKS2_keyslots_size(&cd->u.luks2.hdr));
if (!integrity && sector_size > SECTOR_SIZE) { if (!integrity && sector_size > SECTOR_SIZE) {
dev_size -= (crypt_get_data_offset(cd) * SECTOR_SIZE); dev_size -= (crypt_get_data_offset(cd) * SECTOR_SIZE);
if (dev_size % sector_size) { if (dev_size % sector_size) {