Add crypt_volume_key_get_by_keyslot_context.

Extends avaiable methods for retrieving device volume key.
The volume key now may be extracted using passphrase, keyfile
(passphrase in a file) or token (LUKS2 only).

For LUKS devices, it returns generated volume key after
sucessfull crypt_format where new volume key got generated.

Fixes: #777.
This commit is contained in:
Ondrej Kozina
2022-10-21 14:02:33 +02:00
parent 0e6264c53c
commit f7e2ed956b
5 changed files with 370 additions and 0 deletions

View File

@@ -1666,6 +1666,41 @@ int crypt_volume_key_get(struct crypt_device *cd,
const char *passphrase,
size_t passphrase_size);
/**
* Get volume key from crypt device by keyslot context.
*
* @param cd crypt device handle
* @param keyslot use this keyslot or @e CRYPT_ANY_SLOT
* @param volume_key buffer for volume key
* @param volume_key_size on input, size of buffer @e volume_key,
* on output size of @e volume_key
* @param kc keyslot context used to unlock volume key
*
* @return unlocked key slot number or negative errno otherwise.
*
* @note See @link crypt-keyslot-context-types @endlink for info on keyslot
* context initialization.
* @note For TCRYPT cipher chain is the volume key concatenated
* for all ciphers in chain (kc may be NULL).
* @note For VERITY the volume key means root hash used for activation
* (kc may be NULL).
* @note For LUKS devices, if kc is @e NULL and volume key is cached in
* device context it returns the volume key generated in preceding
* @link crypt_format @endlink call.
* @note @link CRYPT_KC_TYPE_TOKEN @endlink keyslot context is usable only with LUKS2 devices.
* @note @link CRYPT_KC_TYPE_KEY @endlink keyslot context can not be used.
* @note To get LUKS2 unbound key, keyslot parameter must not be @e CRYPT_ANY_SLOT.
* @note EPERM errno means provided keyslot context could not unlock any (or selected)
* keyslot.
* @note ENOENT errno means no LUKS keyslot is available to retrieve volume key from
* and there's no cached volume key in device handle.
*/
int crypt_volume_key_get_by_keyslot_context(struct crypt_device *cd,
int keyslot,
char *volume_key,
size_t *volume_key_size,
struct crypt_keyslot_context *kc);
/**
* Verify that provided volume key is valid for crypt device.
*

View File

@@ -163,4 +163,5 @@ CRYPTSETUP_2.6 {
crypt_keyslot_context_set_pin;
crypt_keyslot_context_get_type;
crypt_keyslot_add_by_keyslot_context;
crypt_volume_key_get_by_keyslot_context;
} CRYPTSETUP_2.5;

View File

@@ -4855,6 +4855,99 @@ int crypt_volume_key_get(struct crypt_device *cd,
return r;
}
int crypt_volume_key_get_by_keyslot_context(struct crypt_device *cd,
int keyslot,
char *volume_key,
size_t *volume_key_size,
struct crypt_keyslot_context *kc)
{
size_t passphrase_size;
int key_len, r;
const char *passphrase = NULL;
struct volume_key *vk = NULL;
if (!cd || !volume_key || !volume_key_size ||
(!kc && !isLUKS(cd->type) && !isTCRYPT(cd->type) && !isVERITY(cd->type)))
return -EINVAL;
if (isLUKS2(cd->type) && keyslot != CRYPT_ANY_SLOT)
key_len = LUKS2_get_keyslot_stored_key_size(&cd->u.luks2.hdr, keyslot);
else
key_len = crypt_get_volume_key_size(cd);
if (key_len < 0)
return -EINVAL;
if (key_len > (int)*volume_key_size) {
log_err(cd, _("Volume key buffer too small."));
return -ENOMEM;
}
if (kc && !kc->get_passphrase)
return -EINVAL;
if (kc && kc->get_passphrase) {
r = kc->get_passphrase(cd, kc, &passphrase, &passphrase_size);
if (r < 0)
return r;
}
r = -EINVAL;
if (isLUKS2(cd->type)) {
if (kc && !kc->get_luks2_key)
log_err(cd, _("Cannot retrieve volume key for LUKS2 device."));
else if (!kc)
r = -ENOENT;
else
r = kc->get_luks2_key(cd, kc, keyslot,
keyslot == CRYPT_ANY_SLOT ? CRYPT_DEFAULT_SEGMENT : CRYPT_ANY_SEGMENT,
&vk);
} else if (isLUKS1(cd->type)) {
if (kc && !kc->get_luks1_volume_key)
log_err(cd, _("Cannot retrieve volume key for LUKS1 device."));
else if (!kc)
r = -ENOENT;
else
r = kc->get_luks1_volume_key(cd, kc, keyslot, &vk);
} else if (isPLAIN(cd->type)) {
if (passphrase && cd->u.plain.hdr.hash)
r = process_key(cd, cd->u.plain.hdr.hash, key_len,
passphrase, passphrase_size, &vk);
if (r < 0)
log_err(cd, _("Cannot retrieve volume key for plain device."));
} else if (isVERITY(cd->type)) {
/* volume_key == root hash */
if (cd->u.verity.root_hash) {
memcpy(volume_key, cd->u.verity.root_hash, cd->u.verity.root_hash_size);
*volume_key_size = cd->u.verity.root_hash_size;
r = 0;
} else
log_err(cd, _("Cannot retrieve root hash for verity device."));
} else if (isTCRYPT(cd->type)) {
r = TCRYPT_get_volume_key(cd, &cd->u.tcrypt.hdr, &cd->u.tcrypt.params, &vk);
} else if (isBITLK(cd->type)) {
if (passphrase)
r = BITLK_get_volume_key(cd, passphrase, passphrase_size, &cd->u.bitlk.params, &vk);
if (r < 0)
log_err(cd, _("Cannot retrieve volume key for BITLK device."));
} else
log_err(cd, _("This operation is not supported for %s crypt device."), cd->type ?: "(none)");
if (r == -ENOENT && isLUKS(cd->type) && cd->volume_key) {
vk = crypt_alloc_volume_key(cd->volume_key->keylength, cd->volume_key->key);
r = vk ? 0 : -ENOMEM;
}
if (r >= 0 && vk) {
memcpy(volume_key, vk->key, vk->keylength);
*volume_key_size = vk->keylength;
}
crypt_free_volume_key(vk);
return r;
}
int crypt_volume_key_verify(struct crypt_device *cd,
const char *volume_key,
size_t volume_key_size)

View File

@@ -4879,6 +4879,125 @@ static void LuksKeyslotAdd(void)
_cleanup_dmdevices();
}
static void VolumeKeyGet(void)
{
struct crypt_params_luks2 params = {
.sector_size = 512
};
char key[256], key2[256];
#ifdef KERNEL_KEYRING
key_serial_t kid;
#endif
const struct crypt_token_params_luks2_keyring tparams = {
.key_description = KEY_DESC_TEST0
};
const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a"
"bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1b";
size_t key_size = strlen(vk_hex) / 2;
const char *cipher = "aes";
const char *cipher_mode = "xts-plain64";
uint64_t r_payload_offset;
struct crypt_keyslot_context *um1, *um2;
crypt_decode_key(key, vk_hex, key_size);
OK_(prepare_keyfile(KEYFILE1, PASSPHRASE1, strlen(PASSPHRASE1)));
#ifdef KERNEL_KEYRING
kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE1, strlen(PASSPHRASE1), KEY_SPEC_THREAD_KEYRING);
NOTFAIL_(kid, "Test or kernel keyring are broken.");
#endif
// init test devices
OK_(get_luks2_offsets(0, 0, 0, NULL, &r_payload_offset));
OK_(create_dmdevice_over_loop(H_DEVICE, r_payload_offset + 1));
// test support for embedded key (after crypt_format)
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(set_fast_pbkdf(cd));
OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, key_size, &params));
key_size--;
FAIL_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL), "buffer too small");
// check cached generated volume key can be retrieved
key_size++;
OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL));
OK_(crypt_volume_key_verify(cd, key2, key_size));
CRYPT_FREE(cd);
// check we can add keyslot via retrieved key
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
OK_(set_fast_pbkdf(cd));
OK_(crypt_keyslot_context_init_by_volume_key(cd, key2, key_size, &um1));
OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um2));
EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 3, um2, 0), 3);
crypt_keyslot_context_free(um1);
crypt_keyslot_context_free(um2);
CRYPT_FREE(cd);
// check selected volume key can be retrieved and added
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(set_fast_pbkdf(cd));
OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
memset(key2, 0, key_size);
OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL));
OK_(memcmp(key, key2, key_size));
OK_(crypt_keyslot_context_init_by_volume_key(cd, key2, key_size, &um1));
OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um2));
EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 0, um2, 0), 0);
crypt_keyslot_context_free(um2);
OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um2));
EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 1, um2, 0), 1);
crypt_keyslot_context_free(um2);
#ifdef KERNEL_KEYRING
EQ_(crypt_token_luks2_keyring_set(cd, 0, &tparams), 0);
EQ_(crypt_token_assign_keyslot(cd, 0, 1), 0);
#endif
crypt_keyslot_context_free(um1);
CRYPT_FREE(cd);
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
// check key context is not usable
OK_(crypt_keyslot_context_init_by_volume_key(cd, key, key_size, &um1));
EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), -EINVAL);
crypt_keyslot_context_free(um1);
// by passphrase
memset(key2, 0, key_size);
OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um1));
EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 0);
OK_(memcmp(key, key2, key_size));
memset(key2, 0, key_size);
EQ_(crypt_volume_key_get_by_keyslot_context(cd, 0, key2, &key_size, um1), 0);
OK_(memcmp(key, key2, key_size));
crypt_keyslot_context_free(um1);
// by keyfile
memset(key2, 0, key_size);
OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um1));
EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 1);
OK_(memcmp(key, key2, key_size));
memset(key2, 0, key_size);
EQ_(crypt_volume_key_get_by_keyslot_context(cd, 1, key2, &key_size, um1), 1);
crypt_keyslot_context_free(um1);
#ifdef KERNEL_KEYRING
// by token
OK_(crypt_keyslot_context_init_by_token(cd, CRYPT_ANY_TOKEN, NULL, NULL, 0, NULL, &um1));
memset(key2, 0, key_size);
EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 1);
OK_(memcmp(key, key2, key_size));
crypt_keyslot_context_free(um1);
#endif
CRYPT_FREE(cd);
_remove_keyfiles();
_cleanup_dmdevices();
}
static int _crypt_load_check(struct crypt_device *cd)
{
#ifdef HAVE_BLKID
@@ -5005,6 +5124,7 @@ int main(int argc, char *argv[])
RUN_(Luks2Reencryption, "LUKS2 reencryption");
#endif
RUN_(LuksKeyslotAdd, "Adding keyslot via new API");
RUN_(VolumeKeyGet, "Getting volume key via keyslot context API");
RUN_(Luks2Repair, "LUKS2 repair"); // test disables metadata locking. Run always last!
_cleanup();

View File

@@ -325,6 +325,7 @@ static void AddDevicePlain(void)
};
int fd;
char key[128], key2[128], path[128];
struct crypt_keyslot_context *kc = NULL;
const char *passphrase = PASSPHRASE;
// hashed hex version of PASSPHRASE
@@ -578,6 +579,11 @@ static void AddDevicePlain(void)
key_size++;
OK_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key2, &key_size, passphrase, strlen(passphrase)));
OK_(memcmp(key, key2, key_size));
memset(key2, 0, key_size);
OK_(crypt_keyslot_context_init_by_passphrase(cd, passphrase, strlen(passphrase), &kc));
OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, kc));
OK_(memcmp(key, key2, key_size));
crypt_keyslot_context_free(kc);
OK_(strcmp(cipher, crypt_get_cipher(cd)));
OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd)));
@@ -1715,6 +1721,10 @@ static void VerityTest(void)
OK_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_out, &root_hash_out_size, NULL, 0));
EQ_(32, root_hash_out_size);
OK_(memcmp(root_hash, root_hash_out, root_hash_out_size));
memset(root_hash_out, 0, root_hash_out_size);
OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, root_hash_out, &root_hash_out_size, NULL));
EQ_(32, root_hash_out_size);
OK_(memcmp(root_hash, root_hash_out, root_hash_out_size));
OK_(crypt_deactivate(cd, CDEVICE_1));
/* hash fail */
@@ -1791,6 +1801,9 @@ static void TcryptTest(void)
key_size++;
OK_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, NULL, 0));
OK_(memcmp(key, key_def, key_size));
memset(key, 0, key_size);
OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key, &key_size, NULL));
OK_(memcmp(key, key_def, key_size));
reset_log();
OK_(crypt_dump(cd));
@@ -1805,6 +1818,7 @@ static void TcryptTest(void)
GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
FAIL_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, NULL, 0), "Need crypt_load");
FAIL_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key, &key_size, NULL), "Need crypt_load");
// check params after init_by_name
OK_(strcmp("xts-plain64", crypt_get_cipher_mode(cd)));
@@ -2185,6 +2199,112 @@ static void LuksKeyslotAdd(void)
_cleanup_dmdevices();
}
static void VolumeKeyGet(void)
{
struct crypt_params_luks1 params = {
.hash = "sha512",
.data_alignment = 2048, // 2M, data offset will be 2048
};
struct crypt_pbkdf_type min_pbkdf2 = {
.type = "pbkdf2",
.hash = "sha256",
.iterations = 1000,
.flags = CRYPT_PBKDF_NO_BENCHMARK
};
char key[128], key2[128];
const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
size_t key_size = strlen(vk_hex) / 2;
const char *cipher = "aes";
const char *cipher_mode = "cbc-essiv:sha256";
uint64_t r_payload_offset;
struct crypt_keyslot_context *um1, *um2;
crypt_decode_key(key, vk_hex, key_size);
OK_(prepare_keyfile(KEYFILE1, PASSPHRASE1, strlen(PASSPHRASE1)));
// init test devices
OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
OK_(create_dmdevice_over_loop(H_DEVICE, r_payload_offset + 1));
// test support for embedded key (after crypt_format)
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
key_size--;
FAIL_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL), "buffer too small");
// check cached generated volume key can be retrieved
key_size++;
OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL));
OK_(crypt_volume_key_verify(cd, key2, key_size));
CRYPT_FREE(cd);
// check we can add keyslot via retrieved key
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
OK_(crypt_keyslot_context_init_by_volume_key(cd, key2, key_size, &um1));
OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um2));
EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 3, um2, 0), 3);
crypt_keyslot_context_free(um1);
crypt_keyslot_context_free(um2);
CRYPT_FREE(cd);
// check selected volume key can be retrieved and added
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
memset(key2, 0, key_size);
OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL));
OK_(memcmp(key, key2, key_size));
OK_(crypt_keyslot_context_init_by_volume_key(cd, key2, key_size, &um1));
OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um2));
EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 0, um2, 0), 0);
crypt_keyslot_context_free(um2);
OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um2));
EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 1, um2, 0), 1);
crypt_keyslot_context_free(um2);
crypt_keyslot_context_free(um1);
CRYPT_FREE(cd);
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
// check key context is not usable
OK_(crypt_keyslot_context_init_by_volume_key(cd, key, key_size, &um1));
EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), -EINVAL);
crypt_keyslot_context_free(um1);
// check token context is not usable
OK_(crypt_keyslot_context_init_by_token(cd, CRYPT_ANY_TOKEN, NULL, NULL, 0, NULL, &um1));
EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), -EINVAL);
crypt_keyslot_context_free(um1);
// by passphrase
memset(key2, 0, key_size);
OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um1));
EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 0);
OK_(memcmp(key, key2, key_size));
memset(key2, 0, key_size);
EQ_(crypt_volume_key_get_by_keyslot_context(cd, 0, key2, &key_size, um1), 0);
OK_(memcmp(key, key2, key_size));
crypt_keyslot_context_free(um1);
// by keyfile
memset(key2, 0, key_size);
OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um1));
EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 1);
OK_(memcmp(key, key2, key_size));
memset(key2, 0, key_size);
EQ_(crypt_volume_key_get_by_keyslot_context(cd, 1, key2, &key_size, um1), 1);
crypt_keyslot_context_free(um1);
CRYPT_FREE(cd);
_remove_keyfiles();
_cleanup_dmdevices();
}
// Check that gcrypt is properly initialised in format
static void NonFIPSAlg(void)
{
@@ -2280,6 +2400,7 @@ int main(int argc, char *argv[])
RUN_(ResizeIntegrityWithKey, "Integrity raw resize with key");
RUN_(WipeTest, "Wipe device");
RUN_(LuksKeyslotAdd, "Adding keyslot via new API");
RUN_(VolumeKeyGet, "Getting volume key via keyslot context API");
_cleanup();
return 0;