zkey-cryptsetup: Support PHMAC integrity with validate command

In case the volume is integrity protected, and the integrity algorithm is
PHMAC, then also validate the integrity key and print its validation
status.

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-15 10:17:41 +01:00
committed by Jan Höppner
parent 20cc99feb7
commit 8dd5758341

View File

@@ -1503,7 +1503,7 @@ static int check_keysize_and_cipher_mode(const u8 *key, size_t keysize)
* be 0). The size of the secure key is keysize minus integrity_keysize.
*/
static int open_keyslot(int keyslot, u8 **key, size_t *keysize,
size_t *integrity_keysize,
size_t *integrity_keysize, char **integrity_spec,
char **password, size_t *password_len,
const char *prompt, bool no_keysize_check)
{
@@ -1611,6 +1611,9 @@ static int open_keyslot(int keyslot, u8 **key, size_t *keysize,
*keysize = vkeysize;
if (integrity_keysize != NULL)
*integrity_keysize = ip.integrity_key_size;
if (integrity_spec != NULL)
*integrity_spec = ip.integrity != NULL ?
util_strdup(ip.integrity) : NULL;
if (password != NULL)
*password = pw;
else
@@ -1628,6 +1631,14 @@ out:
return rc;
}
static int is_integrity_phmac(const char *integrity_spec,
size_t integrity_key_size)
{
if (integrity_spec == NULL || integrity_key_size == 0)
return 0;
return (strncmp(integrity_spec, "phmac(", 6) == 0);
}
/*
* Validate and get a secure key from a key slot. Optionally returns the key
@@ -1640,37 +1651,71 @@ out:
* be 0). The size of the secure key is keysize minus integrity_keysize.
*/
static int validate_keyslot(int keyslot, u8 **key, size_t *keysize,
size_t *intgrity_keysize,
size_t *intgrity_keysize, char **integrity_spec,
char **password, size_t *password_len,
int *is_old_mk, size_t *clear_keysize,
const char *prompt, const char *invalid_msg)
int *is_integrity_old_mk,
size_t *clear_integrity_keysize,
const char *prompt, const char *invalid_msg,
const char *invalid_msg_integrity)
{
size_t vkeysize = 0, ikeysize = 0;
u8 *vkey = NULL;
int rc, is_old;
size_t vkeysize = 0, ikeysize = 0, ekeysize;
int rc, is_old, is_integrity_old = 0;
u8 *vkey = NULL, *ikey = NULL;
char *integrity = NULL;
int is_phmac_integrity;
rc = open_keyslot(keyslot, &vkey, &vkeysize, &ikeysize,
rc = open_keyslot(keyslot, &vkey, &vkeysize, &ikeysize, &integrity,
password, password_len, prompt, false);
if (rc < 0)
return rc;
keyslot = rc;
rc = validate_secure_key(g.pkey_fd, vkey, vkeysize - ikeysize,
clear_keysize, &is_old, NULL, g.verbose);
is_phmac_integrity = is_integrity_phmac(integrity, ikeysize);
ekeysize = vkeysize - ikeysize;
if (is_phmac_integrity)
ikey = vkey + ekeysize;
rc = validate_secure_key(g.pkey_fd, vkey, ekeysize, clear_keysize,
&is_old, NULL, g.verbose);
if (rc != 0) {
if (invalid_msg != NULL)
warnx("%s", invalid_msg);
else
warnx("The secure volume key of device '%s' is not "
warnx("The secure encryption key of device '%s' is not "
"valid", g.pos_arg);
rc = -EINVAL;
goto out;
}
if (is_secure_key(vkey, vkeysize - ikeysize))
pr_verbose("Volume key is currently enciphered with %s "
pr_verbose("Encryption key is currently enciphered with %s "
"master key", is_old ? "OLD" : "CURRENT");
if (is_phmac_integrity) {
rc = validate_secure_key(g.pkey_fd, ikey, ikeysize,
clear_integrity_keysize,
&is_integrity_old, NULL, g.verbose);
if (rc != 0) {
if (invalid_msg_integrity != NULL)
warnx("%s", invalid_msg_integrity);
else
warnx("The secure integrity key of device '%s' "
"is not valid", g.pos_arg);
rc = -EINVAL;
goto out;
}
if (is_secure_key(ikey, ikeysize))
pr_verbose("Integrity key is currently enciphered with "
"%s master key", is_integrity_old ?
"OLD" : "CURRENT");
} else {
if (clear_integrity_keysize != NULL)
*clear_integrity_keysize = 0;
}
if (key != NULL)
*key = vkey;
else
@@ -1682,11 +1727,19 @@ static int validate_keyslot(int keyslot, u8 **key, size_t *keysize,
*intgrity_keysize = ikeysize;
if (is_old_mk != NULL)
*is_old_mk = is_old;
if (is_integrity_old_mk != NULL)
*is_integrity_old_mk = is_integrity_old;
if (integrity_spec != NULL) {
*integrity_spec = integrity;
integrity = NULL;
}
rc = keyslot;
out:
secure_free(vkey, vkeysize);
if (integrity != NULL)
free(integrity);
return rc;
}
@@ -1753,8 +1806,9 @@ static int reencipher_prepare(int token)
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize,
&integrity_keysize, &password, &password_len,
&is_old_mk, NULL, prompt, NULL);
&integrity_keysize, NULL,
&password, &password_len,
&is_old_mk, NULL, NULL, NULL, prompt, NULL, NULL);
free(prompt);
if (rc < 0)
goto out;
@@ -1952,9 +2006,10 @@ static int reencipher_complete(int token)
util_asprintf(&prompt, "Enter passphrase for key slot %d of '%s': ",
tok.original_keyslot, g.pos_arg);
rc = validate_keyslot(tok.unbound_keyslot, &key, &keysize,
&integrity_keysize, &password, &password_len,
&is_old_mk, NULL,
prompt, msg);
&integrity_keysize, NULL,
&password, &password_len,
&is_old_mk, NULL, NULL, NULL,
prompt, msg, NULL);
free(msg);
free(prompt);
if (rc < 0)
@@ -2110,12 +2165,49 @@ static int command_reencipher(void)
static void print_verification_pattern(const char *vp)
{
printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2,
printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2,
vp);
printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2,
printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2,
&vp[VERIFICATION_PATTERN_LEN / 2]);
}
static void print_validation_status(const char *heading, int is_valid,
u8 *key, size_t seckeysize,
size_t clear_keysize, const char *key_type,
u8 mkvp[MKVP_LENGTH], int is_old_mk,
const char *vp)
{
printf(" %s:\n", heading);
printf(" Status: %s\n",
is_valid ? "Valid" : "Invalid");
printf(" Secure key size: %lu bytes\n", seckeysize);
printf(" XTS type key: %s\n",
is_xts_key(key, seckeysize) ? "Yes" : "No");
printf(" Key type: %s\n", key_type);
if (is_valid) {
printf(" Clear key size: %lu bits\n", clear_keysize);
if (is_secure_key(key, seckeysize)) {
printf(" Enciphered with: %s master key "
"(MKVP: %s)\n", is_old_mk ? "OLD" : "CURRENT",
printable_mkvp(get_card_type_for_keytype(
key_type), mkvp));
}
} else {
printf(" Clear key size: (unknown)\n");
if (is_secure_key(key, seckeysize)) {
printf(" Enciphered with: (unknown, "
"MKVP: %s)\n",
printable_mkvp(get_card_type_for_keytype(
key_type), mkvp));
}
}
if (vp != NULL && strlen(vp) > 0)
print_verification_pattern(vp);
else
printf(" Verification pattern: Not available\n");
}
/*
* Command handler for 'validate'.
*
@@ -2125,23 +2217,30 @@ static void print_verification_pattern(const char *vp)
static int command_validate(void)
{
int reenc_pending = 0, vp_tok_avail = 0, is_valid = 0, is_old_mk = 0;
int is_old_integrity_mk = 0, is_phmac_integrity = 0;
const char *key_type, *integrity_key_type = NULL;
u8 *key = NULL, *integrity_key = NULL;
struct reencipher_token reenc_tok;
u8 integrity_mkvp[MKVP_LENGTH];
size_t clear_integrity_keysize;
size_t integrity_keysize = 0;
char *integrity_spec = NULL;
int is_integrity_valid = 0;
struct vp_token vp_tok;
const char *key_type;
u8 mkvp[MKVP_LENGTH];
size_t clear_keysize;
size_t keysize = 0;
size_t seckeysize;
u8 *key = NULL;
const char *vp;
char *prompt;
char *msg;
int token;
int rc;
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &integrity_keysize,
NULL, NULL, prompt, false);
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize,
&integrity_keysize, &integrity_spec, NULL, NULL,
prompt, false);
free(prompt);
if (rc < 0)
goto out;
@@ -2150,12 +2249,24 @@ static int command_validate(void)
if (rc != 0)
goto out;
is_phmac_integrity = is_integrity_phmac(integrity_spec,
integrity_keysize);
seckeysize = keysize - integrity_keysize;
rc = validate_secure_key(g.pkey_fd, key, seckeysize,
&clear_keysize, &is_old_mk, NULL, g.verbose);
is_valid = (rc == 0);
if (is_phmac_integrity) {
integrity_key = key + seckeysize;
rc = validate_secure_key(g.pkey_fd, integrity_key,
integrity_keysize,
&clear_integrity_keysize,
&is_old_integrity_mk, NULL, g.verbose);
is_integrity_valid = (rc == 0);
}
token = find_token(g.cd, PAES_REENC_TOKEN_NAME);
if (token >= 0) {
rc = get_reencipher_token(g.cd, token, &reenc_tok, true);
@@ -2180,37 +2291,47 @@ static int command_validate(void)
}
}
key_type = get_key_type(key, seckeysize);
printf("Validation of secure volume key of device '%s':\n", g.pos_arg);
printf(" Status: %s\n", is_valid ? "Valid" : "Invalid");
printf(" Secure key size: %lu bytes\n", seckeysize);
printf(" XTS type key: %s\n",
is_xts_key(key, seckeysize) ? "Yes" : "No");
printf(" Key type: %s\n", key_type);
if (is_valid) {
printf(" Clear key size: %lu bits\n", clear_keysize);
if (is_secure_key(key, seckeysize)) {
printf(" Enciphered with: %s master key (MKVP: "
"%s)\n", is_old_mk ? "OLD" : "CURRENT",
printable_mkvp(get_card_type_for_keytype(
key_type), mkvp));
}
} else {
printf(" Clear key size: (unknown)\n");
if (is_secure_key(key, seckeysize)) {
printf(" Enciphered with: (unknown, MKVP: %s)\n",
printable_mkvp(get_card_type_for_keytype(
key_type), mkvp));
if (is_phmac_integrity &&
is_secure_key(integrity_key, integrity_keysize)) {
rc = get_master_key_verification_pattern(integrity_key,
integrity_keysize,
integrity_mkvp,
g.verbose);
if (rc != 0) {
warnx("Failed to get the master key verification "
"pattern: %s", strerror(-rc));
goto out;
}
}
if (vp_tok_avail)
print_verification_pattern(vp_tok.verification_pattern);
else if (reenc_pending)
print_verification_pattern(reenc_tok.verification_pattern);
else
printf(" Verification pattern: Not available\n");
key_type = get_key_type(key, seckeysize);
if (is_phmac_integrity)
integrity_key_type = get_key_type(integrity_key,
integrity_keysize);
printf("Validation of secure volume key of device '%s':\n", g.pos_arg);
vp = vp_tok_avail ? vp_tok.verification_pattern :
reenc_pending ?
reenc_tok.verification_pattern : NULL;
print_validation_status("Encryption key", is_valid, key, seckeysize,
clear_keysize, key_type, mkvp, is_old_mk, vp);
if (is_phmac_integrity) {
vp = vp_tok_avail ? vp_tok.int_verification_pattern :
reenc_pending ?
reenc_tok.int_verification_pattern :
NULL;
print_validation_status("Integrity key", is_integrity_valid,
integrity_key, integrity_keysize,
clear_integrity_keysize,
integrity_key_type, integrity_mkvp,
is_old_integrity_mk, vp);
}
if (is_phmac_integrity)
is_valid &= is_integrity_valid;
is_old_mk |= is_old_integrity_mk;
if (reenc_pending)
printf(" Volume key re-enciphering is pending\n");
@@ -2239,6 +2360,8 @@ static int command_validate(void)
out:
secure_free(key, keysize);
if (integrity_spec != NULL)
free(integrity_spec);
return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -2260,8 +2383,8 @@ static int command_setvp(void)
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize,
&integrity_keysize, NULL, NULL, NULL, NULL,
prompt, NULL);
&integrity_keysize, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, prompt, NULL, NULL);
free(prompt);
if (rc < 0)
goto out;
@@ -2366,7 +2489,7 @@ static int command_setkey(void)
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &integrity_keysize,
&password, &password_len, prompt, false);
NULL, &password, &password_len, prompt, false);
free(prompt);
if (rc < 0)
goto out;
@@ -2575,7 +2698,7 @@ static int command_convert(void)
/* Get current (clear) volume key from LUKS2 header */
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, NULL,
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, NULL, NULL,
&password, &password_len, prompt, true);
free(prompt);
if (rc < 0)