From 355700d606e8c25ed3b7921a087bc29ee369748e Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Mon, 11 Mar 2024 14:42:58 +0100 Subject: [PATCH] zkey: Allow to associate a LUKS volume to an AES key and a HMAC key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For combined integrity protected with encryption, a LUKS2 volume can be associated to exactly one AES type key and also to exactly one HMAC type key. For other volume types, a volume can only be associated to exactly one key, either an AES type key, or an HMAC type key. Signed-off-by: Ingo Franzki Reviewed-by: Finn Callies Signed-off-by: Jan Höppner --- zkey/keystore.c | 148 ++++++++++++++++++++++++++++++++++++++++++++---- zkey/zkey.1 | 25 ++++++-- 2 files changed, 156 insertions(+), 17 deletions(-) diff --git a/zkey/keystore.c b/zkey/keystore.c index ccb8015a..1d102c93 100644 --- a/zkey/keystore.c +++ b/zkey/keystore.c @@ -1239,8 +1239,11 @@ struct volume_check { struct keystore *keystore; const char *name; const char *volume; + const char *key_type; + const char *volume_type; bool set; bool nocheck; + unsigned int num_combined; }; /** @@ -1258,21 +1261,92 @@ struct volume_check { */ static int _keystore_volume_check_process(struct keystore *UNUSED(keystore), const char *name, - struct properties *UNUSED(properties), + struct properties *properties, struct key_filenames *UNUSED(file_names), void *private) { struct volume_check *info = (struct volume_check *)private; + char *key_type, *volume_type; + int rc = 0; + + key_type = _keystore_get_key_type(properties); + volume_type = _keystore_get_volume_type(properties); if (info->set) { if (strcmp(name, info->name) == 0) - return 0; + goto out; + } + + if (is_hmac_key_type(info->key_type) && + strcasecmp(info->volume_type, VOLUME_TYPE_LUKS2) == 0) { + /* HMAC key with LUKS2: accept up to one AES key with LUKS2 */ + if (!is_aes_key_type(key_type)) { + warnx("Key '%s' is already associated with volume '%s'", + name, info->volume); + rc = -EINVAL; + goto out; + } + + if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) != 0) { + warnx("Key '%s' is already associated with volume " + "'%s' but the volume type not LUKS2", name, + info->volume); + rc = -EINVAL; + goto out; + } + + if (info->num_combined > 0) { + warnx("More than one AES key is already associated " + "with volume '%s'", info->volume); + rc = -EINVAL; + goto out; + } + + info->num_combined++; + goto out; + } + + if (is_aes_key_type(info->key_type) && + strcasecmp(info->volume_type, VOLUME_TYPE_LUKS2) == 0) { + /* AES key with LUKS2: accept up to one HMAC key with LUKS2 */ + if (!is_hmac_key_type(key_type)) { + warnx("Key '%s' is already associated with volume '%s'", + name, info->volume); + rc = -EINVAL; + goto out; + } + + if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) != 0) { + warnx("Key '%s' is already associated with volume " + "'%s' but the volume type not LUKS2", name, + info->volume); + rc = -EINVAL; + goto out; + } + + if (info->num_combined > 0) { + warnx("More than one HMAC key is already associated " + "with volume '%s'", info->volume); + rc = -EINVAL; + goto out; + } + + info->num_combined++; + goto out; } warnx("Key '%s' is already associated with volume '%s'", name, info->volume); - return -EINVAL; + rc = -EINVAL; + +out: + if (key_type != NULL) + free(key_type); + if (volume_type != NULL) + free(volume_type); + + return rc; } /** @@ -1841,6 +1915,15 @@ static int _keystore_create_info_props(struct keystore *keystore, *props = NULL; + if (volume_type == NULL) + volume_type = is_hmac_key_type(key_type) ? + DEFAULT_INTERITY_VOLUME_TYPE : + DEFAULT_VOLUME_TYPE; + if (!_keystore_valid_volume_type(key_type, volume_type)) { + warnx("Invalid volume-type specified"); + return -EINVAL; + } + key_props = properties_new(); if (is_aes_key_type(key_type)) rc = _keystore_set_default_aes_properties(key_props); @@ -1864,6 +1947,8 @@ static int _keystore_create_info_props(struct keystore *keystore, goto out; } + vol_check.key_type = key_type; + vol_check.volume_type = volume_type; rc = _keystore_change_association(key_props, PROP_NAME_VOLUMES, volumes != NULL ? volumes : "", "volume", _keystore_volume_check, @@ -1891,15 +1976,6 @@ static int _keystore_create_info_props(struct keystore *keystore, goto out; } - if (volume_type == NULL) - volume_type = is_hmac_key_type(key_type) ? - DEFAULT_INTERITY_VOLUME_TYPE : - DEFAULT_VOLUME_TYPE; - if (!_keystore_valid_volume_type(key_type, volume_type)) { - warnx("Invalid volume-type specified"); - rc = -EINVAL; - goto out; - } rc = properties_set2(key_props, PROP_NAME_VOLUME_TYPE, volume_type, true); if (rc != 0) { @@ -2615,7 +2691,9 @@ int keystore_change_key(struct keystore *keystore, const char *name, const char **passphrase_upd = NULL; char *apqns_prop, *key_type = NULL; char *upd_volume_type = NULL; + char *old_volume_type = NULL; const char *null_ptr = NULL; + char *prop_volumes = NULL; char *upd_volumes = NULL; size_t secure_key_size; u8 *secure_key = NULL; @@ -2650,6 +2728,15 @@ int keystore_change_key(struct keystore *keystore, const char *name, } key_type = _keystore_get_key_type(key_props); + old_volume_type = _keystore_get_volume_type(key_props); + + if (volume_type != NULL) { + if (!_keystore_valid_volume_type(key_type, volume_type)) { + warnx("Invalid volume-type specified"); + rc = -EINVAL; + goto out; + } + } if (description != NULL) { rc = properties_set(key_props, PROP_NAME_DESCRIPTION, @@ -2661,6 +2748,10 @@ int keystore_change_key(struct keystore *keystore, const char *name, } if (volumes != NULL) { + vol_check.key_type = key_type; + vol_check.volume_type = volume_type != NULL ? + volume_type : old_volume_type; + rc = _keystore_change_association(key_props, PROP_NAME_VOLUMES, volumes, "volume", _keystore_volume_check, @@ -2746,6 +2837,23 @@ int keystore_change_key(struct keystore *keystore, const char *name, goto out; } + prop_volumes = properties_get(key_props, PROP_NAME_VOLUMES); + + vol_check.volume = prop_volumes; + vol_check.key_type = key_type; + vol_check.volume_type = volume_type; + vol_check.set = true; + vol_check.nocheck = false; + vol_check.num_combined = 0; + + rc = _keystore_process_filtered(keystore, NULL, prop_volumes, + NULL, NULL, NULL, false, false, + _keystore_volume_check_process, + &vol_check); + free(prop_volumes); + if (rc != 0) + goto out; + rc = properties_set2(key_props, PROP_NAME_VOLUME_TYPE, volume_type, true); if (rc != 0) { @@ -2840,6 +2948,8 @@ out: free(secure_key); if (key_type != NULL) free(key_type); + if (old_volume_type != NULL) + free(old_volume_type); if (rc != 0) pr_verbose(keystore, "Failed to change key '%s': %s", @@ -3899,8 +4009,10 @@ int keystore_copy_key(struct keystore *keystore, const char *name, struct key_filenames file_names = { 0 }; struct key_filenames new_names = { 0 }; struct properties *key_prop = NULL; + char *volume_type = NULL; size_t secure_key_size; bool kms_bound = false; + char *key_type = NULL; u8 *secure_key; int rc; @@ -3939,6 +4051,9 @@ int keystore_copy_key(struct keystore *keystore, const char *name, goto out; } + key_type = _keystore_get_key_type(key_prop); + volume_type = _keystore_get_volume_type(key_prop); + secure_key = read_secure_key(file_names.skey_filename, &secure_key_size, keystore->verbose); if (secure_key == NULL) { @@ -3965,6 +4080,9 @@ int keystore_copy_key(struct keystore *keystore, const char *name, goto out; if (volumes != NULL) { + vol_check.key_type = key_type; + vol_check.volume_type = volume_type; + rc = _keystore_change_association(key_prop, PROP_NAME_VOLUMES, volumes, "volume", @@ -4036,6 +4154,10 @@ out: _keystore_free_key_filenames(&new_names); if (key_prop != NULL) properties_free(key_prop); + if (key_type != NULL) + free(key_type); + if (volume_type != NULL) + free(volume_type); if (rc != 0) pr_verbose(keystore, "Failed to copy key '%s'to '%s': %s", @@ -5836,6 +5958,8 @@ static int _keystore_refresh_kms_key(struct keystore *keystore, } if (volumes != NULL) { + vol_check.key_type = key_type; + vol_check.volume_type = volume_type; rc = _keystore_change_association(properties, PROP_NAME_VOLUMES, volumes, "volume", _keystore_volume_check, diff --git a/zkey/zkey.1 b/zkey/zkey.1 index b6f94edd..fb48a0fd 100644 --- a/zkey/zkey.1 +++ b/zkey/zkey.1 @@ -51,10 +51,14 @@ a textual description of the key, can be associated with a secure key. You can associate a secure key with one or multiple cryptographic adapters (APQNs) that are set up with the same CCA or EP11 master key. You can also associate a secure key with one or multiple volumes -(block devices), which are encrypted using \fBdm\-crypt\fP with the secure key. -The volume association also contains the device-mapper name, separated by a -colon, used with \fBdm\-crypt\fP. A specific volume can only be associated with -one secure key. +(block devices), which are encrypted using \fBdm\-crypt\fP or are integrity +protected using \fBdm\-integrity\fP with the secure key. The volume association +also contains the device-mapper name, separated by a colon, used with +\fBdm\-crypt\fP or \fBdm\-integrity\fP. A specific volume can only be associated +with one secure key. Keys of type AES are used for disk encryption, while keys +of type HMAC are used for integrity protection. Combined encryption and +integrity protetion is possible with \fBLUKS2\fP. Here, the same volume can be +associated to an AES-type key and an HMAC-type key. .PP The generated secure key is saved in a file, and contains an AES secure key with a length of 128, 192, or 256 bits, or two concatenated AES secure keys with a @@ -1358,6 +1362,9 @@ encrypted using \fBdm\-crypt\fP with the secure AES key. The volume association also contains the device-mapper name, separated by a colon, used with \fBdm\-crypt\fP. A specific volume can only be associated with a single secure key. +For combined encryption and integrity protection of a volume associate the same +volume to an AES-type key and an HMAC-type key. Both keys must use the +\fBluks2\fP volume type. This option is only used for secure keys contained in the secure key repository. .TP .BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP @@ -1535,6 +1542,9 @@ integrity protected using \fBdm\-integrity\fP with the secure HMAC key. The volume association also contains the device-mapper name, separated by a colon, used with \fBdm\-crypt\fP or \fBdm\-integrity\fP. A specific volume can only be associated with a single secure key. +For combined encryption and integrity protection of a volume associate the same +volume to an AES-type key and an HMAC-type key. Both keys must use the +\fBluks2\fP volume type. This option is only used for secure keys contained in the secure key repository. .TP .BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP @@ -1699,6 +1709,9 @@ To remove a volume from the associated volumes, prefix the volume with a You cannot mix \fI+\fP and \fI\-\fP in one specification. You can either add or remove (or set) the associations with one command. A specific volume can only be associated with a single secure key. +For combined encryption and integrity protection of a volume associate the same +volume to an AES-type key and an HMAC-type key. Both keys must use the +\fBluks2\fP volume type. This option is only used for secure keys contained in the secure key repository. .TP .BR \-a ", " \-\-apqns\~\fI[+|\-]card1.domain1[,card2.domain2[,...]]\fP @@ -2226,7 +2239,9 @@ virtualization secret, or are to be integrity protected using \fBdm\-integrity\fP with the protected virtualization secret. The volume association also contains the device-mapper name, separated by a colon, used with \fBdm\-crypt\fP or \fBdm\-integrity\fP. A specific volume can only be -associated with a single key. +associated with a single key. For combined encryption and integrity protection +of a volume associate the same volume to an AES-type key and an HMAC-type key. +Both keys must use the \fBluks2\fP volume type. .TP .BR \-S ", " \-\-sector\-size\~\fIbytes\fP Specifies the sector size in bytes used with \fBdm\-crypt\fP or