qcrypto-luks: extract store key function

This function will be used later to store
new keys to the luks metadata

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Maxim Levitsky 2019-09-26 00:35:24 +03:00 committed by Daniel P. Berrangé
parent 9fa9c1c28f
commit 3994a7c909
1 changed files with 181 additions and 123 deletions

View File

@ -623,6 +623,176 @@ qcrypto_block_luks_parse_header(QCryptoBlockLUKS *luks, Error **errp)
return 0;
}
/*
* Given a key slot, user password, and the master key,
* will store the encrypted master key there, and update the
* in-memory header. User must then write the in-memory header
*
* Returns:
* 0 if the keyslot was written successfully
* with the provided password
* -1 if a fatal error occurred while storing the key
*/
static int
qcrypto_block_luks_store_key(QCryptoBlock *block,
unsigned int slot_idx,
const char *password,
uint8_t *masterkey,
uint64_t iter_time,
QCryptoBlockWriteFunc writefunc,
void *opaque,
Error **errp)
{
QCryptoBlockLUKS *luks = block->opaque;
QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
g_autofree uint8_t *splitkey = NULL;
size_t splitkeylen;
g_autofree uint8_t *slotkey = NULL;
g_autoptr(QCryptoCipher) cipher = NULL;
g_autoptr(QCryptoIVGen) ivgen = NULL;
Error *local_err = NULL;
uint64_t iters;
int ret = -1;
if (qcrypto_random_bytes(slot->salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
errp) < 0) {
goto cleanup;
}
splitkeylen = luks->header.master_key_len * slot->stripes;
/*
* Determine how many iterations are required to
* hash the user password while consuming 1 second of compute
* time
*/
iters = qcrypto_pbkdf2_count_iters(luks->hash_alg,
(uint8_t *)password, strlen(password),
slot->salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
luks->header.master_key_len,
&local_err);
if (local_err) {
error_propagate(errp, local_err);
goto cleanup;
}
if (iters > (ULLONG_MAX / iter_time)) {
error_setg_errno(errp, ERANGE,
"PBKDF iterations %llu too large to scale",
(unsigned long long)iters);
goto cleanup;
}
/* iter_time was in millis, but count_iters reported for secs */
iters = iters * iter_time / 1000;
if (iters > UINT32_MAX) {
error_setg_errno(errp, ERANGE,
"PBKDF iterations %llu larger than %u",
(unsigned long long)iters, UINT32_MAX);
goto cleanup;
}
slot->iterations =
MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
/*
* Generate a key that we'll use to encrypt the master
* key, from the user's password
*/
slotkey = g_new0(uint8_t, luks->header.master_key_len);
if (qcrypto_pbkdf2(luks->hash_alg,
(uint8_t *)password, strlen(password),
slot->salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
slot->iterations,
slotkey, luks->header.master_key_len,
errp) < 0) {
goto cleanup;
}
/*
* Setup the encryption objects needed to encrypt the
* master key material
*/
cipher = qcrypto_cipher_new(luks->cipher_alg,
luks->cipher_mode,
slotkey, luks->header.master_key_len,
errp);
if (!cipher) {
goto cleanup;
}
ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
luks->ivgen_cipher_alg,
luks->ivgen_hash_alg,
slotkey, luks->header.master_key_len,
errp);
if (!ivgen) {
goto cleanup;
}
/*
* Before storing the master key, we need to vastly
* increase its size, as protection against forensic
* disk data recovery
*/
splitkey = g_new0(uint8_t, splitkeylen);
if (qcrypto_afsplit_encode(luks->hash_alg,
luks->header.master_key_len,
slot->stripes,
masterkey,
splitkey,
errp) < 0) {
goto cleanup;
}
/*
* Now we encrypt the split master key with the key generated
* from the user's password, before storing it
*/
if (qcrypto_block_cipher_encrypt_helper(cipher, block->niv, ivgen,
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
0,
splitkey,
splitkeylen,
errp) < 0) {
goto cleanup;
}
/* Write out the slot's master key material. */
if (writefunc(block,
slot->key_offset_sector *
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
splitkey, splitkeylen,
opaque,
errp) != splitkeylen) {
goto cleanup;
}
slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
if (qcrypto_block_luks_store_header(block, writefunc, opaque, errp) < 0) {
goto cleanup;
}
ret = 0;
cleanup:
if (slotkey) {
memset(slotkey, 0, luks->header.master_key_len);
}
if (splitkey) {
memset(splitkey, 0, splitkeylen);
}
return ret;
}
/*
* Given a key slot, and user password, this will attempt to unlock
* the master encryption key from the key slot.
@ -944,12 +1114,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
QCryptoBlockCreateOptionsLUKS luks_opts;
Error *local_err = NULL;
g_autofree uint8_t *masterkey = NULL;
g_autofree uint8_t *slotkey = NULL;
g_autofree uint8_t *splitkey = NULL;
size_t splitkeylen = 0;
size_t i;
g_autoptr(QCryptoCipher) cipher = NULL;
g_autoptr(QCryptoIVGen) ivgen = NULL;
g_autofree char *password = NULL;
const char *cipher_alg;
const char *cipher_mode;
@ -1172,9 +1338,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
* to use the first key slot */
splitkeylen = luks->header.master_key_len * QCRYPTO_BLOCK_LUKS_STRIPES;
for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
luks->header.key_slots[i].active = i == 0 ?
QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED :
QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
luks->header.key_slots[i].active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
/* This calculation doesn't match that shown in the spec,
@ -1188,107 +1352,6 @@ qcrypto_block_luks_create(QCryptoBlock *block,
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
}
if (qcrypto_random_bytes(luks->header.key_slots[0].salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
errp) < 0) {
goto error;
}
/* Again we determine how many iterations are required to
* hash the user password while consuming 1 second of compute
* time */
iters = qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,
(uint8_t *)password, strlen(password),
luks->header.key_slots[0].salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
luks->header.master_key_len,
&local_err);
if (local_err) {
error_propagate(errp, local_err);
goto error;
}
if (iters > (ULLONG_MAX / luks_opts.iter_time)) {
error_setg_errno(errp, ERANGE,
"PBKDF iterations %llu too large to scale",
(unsigned long long)iters);
goto error;
}
/* iter_time was in millis, but count_iters reported for secs */
iters = iters * luks_opts.iter_time / 1000;
if (iters > UINT32_MAX) {
error_setg_errno(errp, ERANGE,
"PBKDF iterations %llu larger than %u",
(unsigned long long)iters, UINT32_MAX);
goto error;
}
luks->header.key_slots[0].iterations =
MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
/* Generate a key that we'll use to encrypt the master
* key, from the user's password
*/
slotkey = g_new0(uint8_t, luks->header.master_key_len);
if (qcrypto_pbkdf2(luks_opts.hash_alg,
(uint8_t *)password, strlen(password),
luks->header.key_slots[0].salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
luks->header.key_slots[0].iterations,
slotkey, luks->header.master_key_len,
errp) < 0) {
goto error;
}
/* Setup the encryption objects needed to encrypt the
* master key material
*/
cipher = qcrypto_cipher_new(luks_opts.cipher_alg,
luks_opts.cipher_mode,
slotkey, luks->header.master_key_len,
errp);
if (!cipher) {
goto error;
}
ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg,
luks->ivgen_cipher_alg,
luks_opts.ivgen_hash_alg,
slotkey, luks->header.master_key_len,
errp);
if (!ivgen) {
goto error;
}
/* Before storing the master key, we need to vastly
* increase its size, as protection against forensic
* disk data recovery */
splitkey = g_new0(uint8_t, splitkeylen);
if (qcrypto_afsplit_encode(luks_opts.hash_alg,
luks->header.master_key_len,
luks->header.key_slots[0].stripes,
masterkey,
splitkey,
errp) < 0) {
goto error;
}
/* Now we encrypt the split master key with the key generated
* from the user's password, before storing it */
if (qcrypto_block_cipher_encrypt_helper(cipher, block->niv, ivgen,
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
0,
splitkey,
splitkeylen,
errp) < 0) {
goto error;
}
/* The total size of the LUKS headers is the partition header + key
* slot headers, rounded up to the nearest sector, combined with
@ -1313,23 +1376,21 @@ qcrypto_block_luks_create(QCryptoBlock *block,
goto error;
}
if (qcrypto_block_luks_store_header(block, writefunc, opaque, errp) < 0) {
goto error;
}
/* Write out the master key material, starting at the
* sector immediately following the partition header. */
if (writefunc(block,
luks->header.key_slots[0].key_offset_sector *
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
splitkey, splitkeylen,
opaque,
errp) != splitkeylen) {
/* populate the slot 0 with the password encrypted master key*/
/* This will also store the header */
if (qcrypto_block_luks_store_key(block,
0,
password,
masterkey,
luks_opts.iter_time,
writefunc,
opaque,
errp) < 0) {
goto error;
}
memset(masterkey, 0, luks->header.master_key_len);
memset(slotkey, 0, luks->header.master_key_len);
return 0;
@ -1337,9 +1398,6 @@ qcrypto_block_luks_create(QCryptoBlock *block,
if (masterkey) {
memset(masterkey, 0, luks->header.master_key_len);
}
if (slotkey) {
memset(slotkey, 0, luks->header.master_key_len);
}
qcrypto_block_free_cipher(block);
qcrypto_ivgen_free(block->ivgen);