Allow specific integrity key size.

This patch add support for setting of integrity key size
for LUKS2 devices.

It adds new (optional) JSON "key_size" attribute in segment.integrity JSON object.
If not set, the code use hash length size (backward compatible).

For LUKS2, we do not allow smaller keys than 128 bits.

Mostly based on code from Ingo Franzki <ifranzki@linux.ibm.com>
This commit is contained in:
Milan Broz
2024-11-24 15:38:51 +01:00
parent ff3e2c6a43
commit 7b5ac650e5
10 changed files with 97 additions and 41 deletions

View File

@@ -37,6 +37,8 @@
#define LUKS2_DIGEST_MAX 8
#define LUKS2_MIN_INTEGRITY_KEY_BYTES 16
#define CRYPT_ANY_SEGMENT -1
#define CRYPT_DEFAULT_SEGMENT -2
#define CRYPT_ONE_SEGMENT -3
@@ -377,6 +379,7 @@ int LUKS2_generate_hdr(
const struct volume_key *vk,
const char *cipher_spec,
const char *integrity,
uint32_t integrity_key_size, /* in bytes, only if separate (HMAC) */
const char *uuid,
unsigned int sector_size,
uint64_t data_offset,
@@ -404,6 +407,7 @@ int LUKS2_get_data_size(struct luks2_hdr *hdr, uint64_t *size, bool *dynamic);
uint32_t LUKS2_get_sector_size(struct luks2_hdr *hdr);
const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment);
const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment);
int LUKS2_get_integrity_key_size(struct luks2_hdr *hdr, int segment);
int LUKS2_keyslot_params_default(struct crypt_device *cd, struct luks2_hdr *hdr,
struct luks2_keyslot_params *params);
int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment);

View File

@@ -292,7 +292,7 @@ void json_segment_remove_flag(json_object *jobj_segment, const char *flag);
uint64_t json_segments_get_minimal_offset(json_object *jobj_segments, unsigned blockwise);
json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length, unsigned reencryption);
json_object *json_segment_create_crypt(uint64_t offset, uint64_t iv_offset, const uint64_t *length,
const char *cipher, const char *integrity,
const char *cipher, const char *integrity, uint32_t integrity_key_size,
uint32_t sector_size, unsigned reencryption);
json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length,
uint32_t segment_number, uint32_t key_size);

View File

@@ -193,6 +193,7 @@ int LUKS2_generate_hdr(
const struct volume_key *vk,
const char *cipher_spec,
const char *integrity,
uint32_t integrity_key_size, /* in bytes, only if separate (HMAC) */
const char *uuid,
unsigned int sector_size, /* in bytes */
uint64_t data_offset, /* in bytes */
@@ -279,8 +280,8 @@ int LUKS2_generate_hdr(
if (!opal_key_size)
jobj_segment = json_segment_create_crypt(data_offset, 0,
NULL, cipher_spec,
integrity, sector_size,
0);
integrity, integrity_key_size,
sector_size, 0);
else if (opal_key_size && cipher_spec)
jobj_segment = json_segment_create_opal_crypt(data_offset, &device_size_bytes,
opal_segment_number, opal_key_size, 0,

View File

@@ -2129,6 +2129,10 @@ static void hdr_dump_segments(struct crypt_device *cd, json_object *hdr_jobj)
json_object_object_get_ex(jobj1, "type", &jobj2))
log_std(cd, "\tintegrity: %s\n", json_object_get_string(jobj2));
if (json_object_object_get_ex(jobj_segment, "integrity", &jobj1) &&
json_object_object_get_ex(jobj1, "key_size", &jobj2))
log_std(cd, "\tintegrity key size: %" PRIu32 " [bits]\n", crypt_jobj_get_uint32(jobj2) * 8);
if (json_object_object_get_ex(jobj_segment, "flags", &jobj1) &&
(flags = (int)json_object_array_length(jobj1)) > 0) {
jobj2 = json_object_array_get_idx(jobj1, 0);
@@ -2364,6 +2368,24 @@ const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment)
return json_object_get_string(jobj3);
}
int LUKS2_get_integrity_key_size(struct luks2_hdr *hdr, int segment)
{
json_object *jobj1, *jobj2, *jobj3;
jobj1 = LUKS2_get_segment_jobj(hdr, segment);
if (!jobj1)
return -1;
if (!json_object_object_get_ex(jobj1, "integrity", &jobj2))
return -1;
/* The value is optional, do not fail if not present */
if (!json_object_object_get_ex(jobj2, "key_size", &jobj3))
return 0;
return json_object_get_int(jobj3);
}
/* FIXME: this only ensures that once we have journal encryption, it is not ignored. */
/* implement segment count and type restrictions (crypt and only single crypt) */
static int LUKS2_integrity_compatible(struct luks2_hdr *hdr)
@@ -2715,7 +2737,7 @@ int LUKS2_activate(struct crypt_device *cd,
crypt_key, crypt_get_cipher_spec(cd),
crypt_get_iv_offset(cd), crypt_get_data_offset(cd),
crypt_get_integrity(cd) ?: "none",
crypt_get_integrity_tag_size(cd), 0, /* FIXME */
crypt_get_integrity_key_size(cd), crypt_get_integrity_tag_size(cd),
crypt_get_sector_size(cd));
} else
r = dm_linear_target_set(&dmd.segment, 0,

View File

@@ -310,7 +310,7 @@ static json_object *reencrypt_make_hot_segments_encrypt_shift(struct luks2_hdr *
rh->offset >> SECTOR_SHIFT,
&rh->length,
reencrypt_segment_cipher_new(hdr),
NULL, /* integrity */
NULL, 0, /* integrity */
reencrypt_get_sector_size_new(hdr),
1);
@@ -366,7 +366,7 @@ static json_object *reencrypt_make_segment_new(struct crypt_device *cd,
crypt_get_iv_offset(cd) + (iv_offset >> SECTOR_SHIFT),
segment_length,
reencrypt_segment_cipher_new(hdr),
NULL, /* integrity */
NULL, 0, /* integrity */
reencrypt_get_sector_size_new(hdr), 0);
case CRYPT_REENCRYPT_DECRYPT:
return json_segment_create_linear(data_offset + segment_offset, segment_length, 0);
@@ -478,7 +478,7 @@ static json_object *reencrypt_make_segment_reencrypt(struct crypt_device *cd,
crypt_get_iv_offset(cd) + (iv_offset >> SECTOR_SHIFT),
segment_length,
reencrypt_segment_cipher_new(hdr),
NULL, /* integrity */
NULL, 0, /* integrity */
reencrypt_get_sector_size_new(hdr), 1);
case CRYPT_REENCRYPT_DECRYPT:
return json_segment_create_linear(data_offset + segment_offset, segment_length, 1);
@@ -503,7 +503,7 @@ static json_object *reencrypt_make_segment_old(struct crypt_device *cd,
crypt_get_iv_offset(cd) + (segment_offset >> SECTOR_SHIFT),
segment_length,
reencrypt_segment_cipher_old(hdr),
NULL, /* integrity */
NULL, 0, /* integrity */
reencrypt_get_sector_size_old(hdr),
0);
break;
@@ -2027,7 +2027,7 @@ static int reencrypt_set_decrypt_shift_segments(struct crypt_device *cd,
r = -EINVAL;
jobj_segment_first = json_segment_create_crypt(0, crypt_get_iv_offset(cd),
&moved_segment_length, crypt_get_cipher_spec(cd),
NULL, crypt_get_sector_size(cd), 0);
NULL, 0, crypt_get_sector_size(cd), 0);
if (!jobj_segment_first) {
log_dbg(cd, "Failed generate 1st segment.");
@@ -2043,7 +2043,7 @@ static int reencrypt_set_decrypt_shift_segments(struct crypt_device *cd,
crypt_get_iv_offset(cd) + (moved_segment_length >> SECTOR_SHIFT),
NULL,
crypt_get_cipher_spec(cd),
NULL, /* integrity */
NULL, 0, /* integrity */
crypt_get_sector_size(cd), 0);
if (!jobj_segment_second) {
r = -EINVAL;
@@ -2532,7 +2532,7 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd,
json_segment_get_iv_offset(jobj_tmp),
device_size ? &device_size : NULL,
json_segment_get_cipher(jobj_tmp),
NULL, /* integrity */
NULL, 0, /* integrity */
json_segment_get_sector_size(jobj_tmp),
0);
} else {
@@ -2579,7 +2579,7 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd,
}
jobj_segment_new = json_segment_create_crypt(segment_offset,
crypt_get_iv_offset(cd),
NULL, cipher, NULL, sector_size, 0);
NULL, cipher, NULL, 0, sector_size, 0);
} else if (params->mode == CRYPT_REENCRYPT_DECRYPT) {
segment_offset = data_offset;
if (modify_offset(&segment_offset, data_shift, params->direction)) {

View File

@@ -270,7 +270,7 @@ json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length,
}
static bool json_add_crypt_fields(json_object *jobj_segment, uint64_t iv_offset,
const char *cipher, const char *integrity,
const char *cipher, const char *integrity, uint32_t integrity_key_size,
uint32_t sector_size, unsigned reencryption)
{
json_object *jobj_integrity;
@@ -289,6 +289,8 @@ static bool json_add_crypt_fields(json_object *jobj_segment, uint64_t iv_offset,
json_object_object_add(jobj_integrity, "type", json_object_new_string(integrity));
json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string("none"));
json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string("none"));
if (integrity_key_size)
json_object_object_add(jobj_integrity, "key_size", json_object_new_int(integrity_key_size));
json_object_object_add(jobj_segment, "integrity", jobj_integrity);
}
@@ -300,7 +302,7 @@ static bool json_add_crypt_fields(json_object *jobj_segment, uint64_t iv_offset,
json_object *json_segment_create_crypt(uint64_t offset,
uint64_t iv_offset, const uint64_t *length,
const char *cipher, const char *integrity,
const char *cipher, const char *integrity, uint32_t integrity_key_size,
uint32_t sector_size, unsigned reencryption)
{
json_object *jobj = _segment_create_generic("crypt", offset, length);
@@ -308,7 +310,7 @@ json_object *json_segment_create_crypt(uint64_t offset,
if (!jobj)
return NULL;
if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption))
if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, integrity_key_size, sector_size, reencryption))
return jobj;
json_object_put(jobj);
@@ -350,7 +352,7 @@ json_object *json_segment_create_opal_crypt(uint64_t offset, const uint64_t *len
json_add_opal_fields(jobj, length, segment_number, key_size);
if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption))
if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, 0, sector_size, reencryption))
return jobj;
json_object_put(jobj);