zkey: Support LUKS2 with combined encryption and integrity

The commands 'zkey cryptsetup' generates commands for formatting and
opening volumes of type PLAIN and LUKS2.

For LUKS2, if there exists an HMAC key that is associated to the same
volume as the AES key, generate a 'cryptsetup luksFormat' command for
combined encryption and integrity protection. This uses the '--integrity'
and '--integrity-key-size' options of the 'cryptsetup luksFormat'
command to specify the integrity settings.

The volume key specified with '--master-key-file' must contain the
encryption key and the integrity key concatenated to each other. The
size of the volume key specified with '--key-size' however must be the
size of the encryption key only, in bits. The 'cryptsetup luksFormat'
command will internally read the whole file, use the first part as
encryption key, and the second part as integrity key. The size of the
second part must be specified with the '--integrity-key-size'.

Note: This requires 'wrapped integrity key' support in the cryptsetup
package, as well as in the dm-crypt kernel module.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Ingo Franzki
2024-03-13 11:02:10 +01:00
committed by Jan Höppner
parent c161c04392
commit 103a99fa34
2 changed files with 145 additions and 4 deletions

View File

@@ -89,6 +89,8 @@ struct key_filenames {
static int _keystore_kms_key_unbind(struct keystore *keystore,
struct properties *properties);
static char *_keystore_build_hmac_spec(struct properties *properties,
size_t bitsize);
/**
* Gets the file names of the .skey and .info and .renc files for a named
@@ -4543,6 +4545,55 @@ static int _keystore_execute_cmd(const char *cmd,
return rc;
}
struct find_integrity_key_info {
bool found;
char *integrity_spec;
char *integrity_key_name;
size_t integrity_key_size;
};
static int _keystore_find_integrity_key_cb(struct keystore *keystore,
const char *UNUSED(name),
struct properties *properties,
struct key_filenames *file_names,
void *private)
{
struct find_integrity_key_info *find_info = private;
size_t secure_key_size;
u8 *secure_key = NULL;
size_t bitsize = 0;
int rc;
if (find_info->found)
return 0;
secure_key = read_secure_key(file_names->skey_filename,
&secure_key_size, keystore->verbose);
if (secure_key == NULL)
return -EIO;
rc = get_key_bit_size(secure_key, secure_key_size, &bitsize);
if (rc != 0)
goto out;
find_info->integrity_spec = _keystore_build_hmac_spec(properties,
bitsize);
if (find_info->integrity_spec == NULL) {
rc = -EINVAL;
goto out;
}
find_info->integrity_key_name = util_strdup(file_names->skey_filename);
find_info->integrity_key_size = secure_key_size;
find_info->found = true;
out:
if (secure_key != NULL)
free(secure_key);
return rc;
}
struct crypt_info {
bool integrity;
bool execute;
@@ -4595,13 +4646,17 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
const char *passphrase_file,
struct crypt_info *info)
{
struct find_integrity_key_info find_info = { 0 };
char *keyfile_opt = NULL, *offset_opt = NULL;
char *size_opt = NULL, *tries_opt = NULL;
char *common_passphrase_options;
char *integrity_opts = NULL;
char *combined_key = NULL;
size_t common_len;
char *cmd = NULL;
char temp[100];
char *tmpdir;
int rc = 0;
char *cmd;
sprintf(temp, "--sector-size %lu ", sector_size);
@@ -4665,6 +4720,57 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
printf("%s\n", cmd);
}
} else {
rc = _keystore_process_filtered(keystore,
NULL, volume, NULL,
VOLUME_TYPE_LUKS2,
KEY_TYPE_FILTER_HMAC,
false, false,
_keystore_find_integrity_key_cb,
&find_info);
if (rc != 0)
goto out;
if (find_info.found) {
util_asprintf(&integrity_opts,
"--integrity %s "
"--integrity-key-size %lu ",
find_info.integrity_spec,
find_info.integrity_key_size * 8);
/*
* The volume key for a volume with combined
* encryption and integrity protection is built
* by concatenating the integrity key to the
* encryption key. The size of the volume key
* specified by --key-size is however just the
* encryption key size in bits.
*/
tmpdir = secure_getenv("TMPDIR");
util_asprintf(&combined_key,
"%s/luks2-integrity-%s.skey",
tmpdir != NULL ? tmpdir : "/tmp",
dmname);
util_asprintf(&cmd, "cat '%s' '%s' > '%s'",
key_file_name,
find_info.integrity_key_name,
combined_key);
if (info->execute) {
printf("Executing: %s\n", cmd);
rc = _keystore_execute_cmd(cmd, "cat");
} else {
printf("%s\n", cmd);
}
free(cmd);
cmd = NULL;
if (rc != 0)
goto out;
key_file_name = combined_key;
}
/*
* Use Argon2i as key derivation function for LUKS2
* volumes, but with options for low memory and time requirements.
@@ -4678,7 +4784,7 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
util_asprintf(&cmd,
"cryptsetup luksFormat %s%s--type luks2 "
"--volume-key-file '%s' --key-size %lu "
"--cipher %s --pbkdf %s %s%s%s",
"--cipher %s --pbkdf %s %s%s%s%s",
info->batch_mode ? "-q " : "",
keystore->verbose ? "-v " : "",
key_file_name, key_file_size * 8,
@@ -4686,6 +4792,8 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
info->fips ? "pbkdf2" :
"argon2i --pbkdf-memory 32 "
"--pbkdf-force-iterations 4",
integrity_opts != NULL ?
integrity_opts : "",
common_len > 0 ?
common_passphrase_options : "",
sector_size > 0 ? temp : "", volume);
@@ -4698,8 +4806,16 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
}
free(cmd);
cmd = NULL;
if (rc != 0)
return rc;
goto out;
if (combined_key != NULL && !info->execute) {
util_asprintf(&cmd, "rm '%s' -f", combined_key);
printf("%s\n", cmd);
free(cmd);
cmd = NULL;
}
util_asprintf(&cmd,
"zkey-cryptsetup setvp %s %s%s", volume,
@@ -4716,7 +4832,20 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
}
}
} else {
return -EINVAL;
rc = -EINVAL;
}
out:
if (find_info.integrity_spec != NULL)
free(find_info.integrity_spec);
if (find_info.integrity_key_name != NULL)
free(find_info.integrity_key_name);
if (integrity_opts != NULL)
free(integrity_opts);
if (combined_key != NULL) {
if (info->execute)
remove(combined_key);
free(combined_key);
}
free(common_passphrase_options);

View File

@@ -801,6 +801,18 @@ might be disabled by a policy when FIPS mode is active. Because PAES uses secure
AES keys as volume keys, the security of the key derivation function used to
encrypt the volume key in the LUKS key slots is of less relevance.
.P
For combined encryption and integrity protection of a volume, the same volume
must be assigned to an AES-type key and an HMAC-type key. Both keys must use the
\fBluks2\fP volume type. For such a volume, the generated \fBcryptsetup
luksFormat\fP command contains the \fB\-\-integrity\fP option to enable
integrity protection. An additional command is generated prior to the
\fBcryptsetup luksFormat\fP command to concatenate the encryption key and the
integrity key to each other into a temporary file. This temporary file is then
used with the \fBcryptsetup luksFormat\fP command as volume key. When the
\fP\-\-run\fP option is specified, the temporary file is removed automatically
after running the commands, otherwise a command to remove the temporary file is
generated, too.
.P
For LUKS2 volumes, a passphrase is required. You are prompted for the
passphrase when running the generated commands, unless option
.B \-\-key\-file