Commit 5fc79f56 authored by Milan Broz's avatar Milan Broz

Move PBKDF internal benchmark to one place.

Also cache its value in active context, so we run benchmark
only once.

The patch also changes calculated value for LUKS1 key digest
to 125 miliseconds (it means that for full 8 used slots
the additional slow-down is circa 1 second).

Note that there is no need to have too high iteration count
for key digest; if it is too computationally expensive, attacker
will better decrypt of one sector with candidate key anyway.
(Check for a known signature.)

The reason to have some delay for key digest check was
to complicate brute-force search for volume key with LUKS header
only (and if RNG used to generate volumekey was flawed
allowing such a search i reasonable time).
parent f4bb2ad6
......@@ -49,8 +49,9 @@
#define DEFAULT_MEM_ALIGNMENT 4096
#define MAX_ERROR_LENGTH 512
#define MAX_PBKDF_THREADS 8
#define MAX_PBKDF_THREADS 4
#define MAX_PBKDF_MEMORY 1024*1024 /* 1GiB */
#define MIN_PBKDF2_ITERATIONS 1000 /* recommendation in NIST SP 800-132 */
#define at_least(a, b) ({ __typeof__(a) __at_least = (a); (__at_least >= (b))?__at_least:(b); })
......@@ -65,6 +66,10 @@ struct volume_key *crypt_alloc_volume_key(size_t keylength, const char *key);
struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, size_t keylength);
void crypt_free_volume_key(struct volume_key *vk);
int crypt_benchmark_pbkdf_internal(struct crypt_device *cd,
struct crypt_pbkdf_type *pbkdf,
size_t volume_key_size);
/* Device backend */
struct device;
int device_alloc(struct device **device, const char *path);
......
......@@ -213,7 +213,8 @@ struct crypt_pbkdf_type {
const char *type; /**< PBKDF algorithm */
const char *hash; /**< Hash algorithm */
uint32_t time_ms; /**< Requested time cost [milliseconds] */
uint32_t max_memory_kb; /**< Requested memory cost [kilobytes] */
uint32_t iterations; /**< Iterations, 0 or benchmarked value. */
uint32_t max_memory_kb; /**< Requested or benchmarked memory cost [kilobytes] */
uint32_t parallel_threads;/**< Requested parallel cost [threads] */
};
......
......@@ -386,7 +386,6 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx)
struct luks_phdr temp_phdr;
const unsigned char *sector = (const unsigned char*)phdr;
struct volume_key *vk;
uint32_t PBKDF2_per_sec = 1;
int i, bad, r, need_write = 0;
if (phdr->keyBytes != 16 && phdr->keyBytes != 32 && phdr->keyBytes != 64) {
......@@ -409,7 +408,6 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx)
r = LUKS_generate_phdr(&temp_phdr, vk, phdr->cipherName, phdr->cipherMode,
phdr->hashSpec,phdr->uuid, LUKS_STRIPES,
phdr->payloadOffset, 0,
1, &PBKDF2_per_sec,
1, ctx);
if (r < 0)
goto out;
......@@ -718,8 +716,6 @@ int LUKS_generate_phdr(struct luks_phdr *header,
const char *uuid, unsigned int stripes,
unsigned int alignPayload,
unsigned int alignOffset,
uint32_t iteration_time_ms,
uint32_t *PBKDF2_per_sec,
int detached_metadata_device,
struct crypt_device *ctx)
{
......@@ -727,11 +723,8 @@ int LUKS_generate_phdr(struct luks_phdr *header,
size_t blocksPerStripeSet, currentSector;
int r;
uuid_t partitionUuid;
struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2,
.hash = hashSpec,
.time_ms = 1000,
};
struct crypt_pbkdf_type *pbkdf;
double PBKDF2_temp;
char luksMagic[] = LUKS_MAGIC;
/* For separate metadata device allow zero alignment */
......@@ -784,19 +777,17 @@ int LUKS_generate_phdr(struct luks_phdr *header,
return r;
}
r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength,
NULL, NULL);
if (r < 0) {
log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"),
header->hashSpec);
/* Compute master key digest */
pbkdf = CONST_CAST(struct crypt_pbkdf_type *)crypt_get_pbkdf_type(ctx);
r = crypt_benchmark_pbkdf_internal(ctx, pbkdf, vk->keylength);
if (r < 0)
return r;
}
assert(pbkdf->iterations);
/* Compute master key digest */
*PBKDF2_per_sec = pbkdf.time_ms;
iteration_time_ms /= 8;
header->mkDigestIterations = at_least((uint32_t)(*PBKDF2_per_sec/1024) * iteration_time_ms,
LUKS_MKD_ITERATIONS_MIN);
PBKDF2_temp = (double)pbkdf->iterations * LUKS_MKD_ITERATIONS_MS / pbkdf->time_ms;
if (PBKDF2_temp > (double)UINT32_MAX)
return -EINVAL;
header->mkDigestIterations = at_least((uint32_t)PBKDF2_temp, LUKS_MKD_ITERATIONS_MIN);
r = crypt_pbkdf(CRYPT_KDF_PBKDF2, header->hashSpec, vk->key,vk->keylength,
header->mkDigestSalt, LUKS_SALTSIZE,
......@@ -857,20 +848,12 @@ int LUKS_hdr_uuid_set(
int LUKS_set_key(unsigned int keyIndex,
const char *password, size_t passwordLen,
struct luks_phdr *hdr, struct volume_key *vk,
uint32_t iteration_time_ms,
uint32_t *PBKDF2_per_sec,
struct crypt_device *ctx)
{
struct volume_key *derived_key;
char *AfKey = NULL;
size_t AFEKSize;
double PBKDF2_temp;
struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2,
.hash = hdr->hashSpec,
.time_ms = 1000,
};
struct crypt_pbkdf_type *pbkdf;
int r;
if(hdr->keyblock[keyIndex].active != LUKS_KEY_DISABLED) {
......@@ -886,25 +869,19 @@ int LUKS_set_key(unsigned int keyIndex,
}
log_dbg("Calculating data for key slot %d", keyIndex);
r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength,
NULL, NULL);
if (r < 0) {
log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"),
hdr->hashSpec);
pbkdf = CONST_CAST(struct crypt_pbkdf_type *)crypt_get_pbkdf_type(ctx);
r = crypt_benchmark_pbkdf_internal(ctx, pbkdf, vk->keylength);
if (r < 0)
return r;
}
assert(pbkdf->iterations);
/*
* Final iteration count is at least LUKS_SLOT_ITERATIONS_MIN
*/
*PBKDF2_per_sec = pbkdf.time_ms;
PBKDF2_temp = ((double)*PBKDF2_per_sec * iteration_time_ms / 1000.);
assert(PBKDF2_temp < UINT32_MAX);
hdr->keyblock[keyIndex].passwordIterations = at_least((uint32_t)PBKDF2_temp,
LUKS_SLOT_ITERATIONS_MIN);
log_dbg("Key slot %d use %" PRIu32 " password iterations.", keyIndex, hdr->keyblock[keyIndex].passwordIterations);
hdr->keyblock[keyIndex].passwordIterations =
at_least(pbkdf->iterations, LUKS_SLOT_ITERATIONS_MIN);
log_dbg("Key slot %d use %" PRIu32 " password iterations.", keyIndex,
hdr->keyblock[keyIndex].passwordIterations);
derived_key = crypt_alloc_volume_key(hdr->keyBytes, NULL);
if (!derived_key)
......
......@@ -40,6 +40,9 @@
#define LUKS_MKD_ITERATIONS_MIN 1000
#define LUKS_SLOT_ITERATIONS_MIN 1000
// Iteration time for digest in ms
#define LUKS_MKD_ITERATIONS_MS 125
#define LUKS_KEY_DISABLED_OLD 0
#define LUKS_KEY_ENABLED_OLD 0xCAFE
......@@ -106,8 +109,6 @@ int LUKS_generate_phdr(
unsigned int stripes,
unsigned int alignPayload,
unsigned int alignOffset,
uint32_t iteration_time_ms,
uint32_t *PBKDF2_per_sec,
int detached_metadata_device,
struct crypt_device *ctx);
......@@ -147,8 +148,6 @@ int LUKS_set_key(
size_t passwordLen,
struct luks_phdr *hdr,
struct volume_key *vk,
uint32_t iteration_time_ms,
uint32_t *PBKDF2_per_sec,
struct crypt_device *ctx);
int LUKS_open_key_with_hdr(
......
......@@ -56,7 +56,6 @@ struct crypt_device {
union {
struct { /* used in CRYPT_LUKS1 */
struct luks_phdr hdr;
uint32_t PBKDF2_per_sec;
} luks1;
struct { /* used in CRYPT_PLAIN */
struct crypt_params_plain hdr;
......@@ -550,21 +549,23 @@ int crypt_set_data_device(struct crypt_device *cd, const char *device)
/*
* PBKDF configuration interface
*/
static int verify_pbkdf_params(struct crypt_device *cd, const struct crypt_pbkdf_type *pbkdf)
static int verify_pbkdf_params(struct crypt_device *cd,
const struct crypt_pbkdf_type *pbkdf)
{
const char *pbkdf_type;
int r = 0;
if (!pbkdf->type || !pbkdf->hash)
if (!pbkdf->type || !pbkdf->hash || !pbkdf->time_ms)
return -EINVAL;
/* TODO: initialise crypto and check the hash and pbkdf are both available */
r = crypt_parse_pbkdf(pbkdf->type, NULL);
r = crypt_parse_pbkdf(pbkdf->type, &pbkdf_type);
if (r < 0) {
log_err(cd, _("Unknown pbkdf type %s.\n"), pbkdf->type);
log_err(cd, _("Unknown PBKDF type %s.\n"), pbkdf->type);
return r;
}
if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
if (!strcmp(pbkdf_type, CRYPT_KDF_PBKDF2)) {
if (pbkdf->max_memory_kb || pbkdf->parallel_threads) {
log_err(cd, _("PBKDF max memory or parallel threads must not be set with pbkdf2.\n"));
return -EINVAL;
......@@ -596,13 +597,13 @@ static int verify_pbkdf_params(struct crypt_device *cd, const struct crypt_pbkdf
static int init_pbkdf_type(struct crypt_device *cd, const struct crypt_pbkdf_type *pbkdf)
{
const char *hash, *type;
unsigned cpus;
int r;
struct crypt_pbkdf_type default_luks1 = {
.type = CRYPT_KDF_PBKDF2,
.hash = DEFAULT_LUKS1_HASH,
.time_ms = cd->iter_time_set ? cd->pbkdf.time_ms : DEFAULT_LUKS1_ITER_TIME
};
unsigned cpus = crypt_cpusonline();
if (!pbkdf) {
pbkdf = &default_luks1;
......@@ -635,15 +636,28 @@ static int init_pbkdf_type(struct crypt_device *cd, const struct crypt_pbkdf_typ
cd->pbkdf.type = type;
cd->pbkdf.hash = hash;
/* Reset iteration count so benchmark must run again. */
cd->pbkdf.iterations = 0;
cd->pbkdf.time_ms = pbkdf->time_ms;
cd->pbkdf.max_memory_kb = pbkdf->max_memory_kb;
cd->pbkdf.parallel_threads = pbkdf->parallel_threads;
if (pbkdf->parallel_threads > cpus) {
cd->pbkdf.parallel_threads = cpus;
log_dbg("Only %u active CPUs detected, PBKDF threads decreased from %d to %d.",
cpus, pbkdf->parallel_threads, cpus);
} else
cd->pbkdf.parallel_threads = pbkdf->parallel_threads;
if (cd->pbkdf.parallel_threads > MAX_PBKDF_THREADS) {
log_dbg("Maximum PBKDF threads is %d (requested %d).",
MAX_PBKDF_THREADS, cd->pbkdf.parallel_threads);
cd->pbkdf.parallel_threads = MAX_PBKDF_THREADS;
}
if (cd->pbkdf.parallel_threads) {
cpus = crypt_cpusonline();
if (cd->pbkdf.parallel_threads > cpus) {
log_dbg("Only %u active CPUs detected, "
"PBKDF threads decreased from %d to %d.",
cpus, cd->pbkdf.parallel_threads, cpus);
cd->pbkdf.parallel_threads = cpus;
}
}
return 0;
}
......@@ -677,7 +691,7 @@ int crypt_set_pbkdf_type(struct crypt_device *cd, const struct crypt_pbkdf_type
const struct crypt_pbkdf_type *crypt_get_pbkdf_type(struct crypt_device *cd)
{
return cd ? &cd->pbkdf : NULL;
return (cd && cd->pbkdf.type) ? &cd->pbkdf : NULL;
}
void crypt_set_iteration_time(struct crypt_device *cd, uint64_t iteration_time_ms)
......@@ -709,6 +723,12 @@ static int _crypt_load_luks1(struct crypt_device *cd, int require_header, int re
if (r < 0)
return r;
if (repair && !cd->pbkdf.type) {
r = init_pbkdf_type(cd, NULL);
if (r)
return r;
}
r = LUKS_read_phdr(&hdr, require_header, repair, cd);
if (r < 0)
return r;
......@@ -1297,7 +1317,6 @@ static int _crypt_format_luks1(struct crypt_device *cd,
cd->pbkdf.hash, uuid, LUKS_STRIPES,
required_alignment / SECTOR_SIZE,
alignment_offset / SECTOR_SIZE,
cd->pbkdf.time_ms, &cd->u.luks1.PBKDF2_per_sec,
cd->metadata_device ? 1 : 0, cd);
if (r < 0)
return r;
......@@ -2028,8 +2047,8 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd,
if(r < 0)
goto out;
r = LUKS_set_key(keyslot, CONST_CAST(char*)new_passphrase, new_passphrase_size,
&cd->u.luks1.hdr, vk, cd->pbkdf.time_ms, &cd->u.luks1.PBKDF2_per_sec, cd);
r = LUKS_set_key(keyslot, CONST_CAST(char*)new_passphrase,
new_passphrase_size, &cd->u.luks1.hdr, vk, cd);
if(r < 0)
goto out;
......@@ -2080,8 +2099,7 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd,
}
r = LUKS_set_key(keyslot_new, new_passphrase, new_passphrase_size,
&cd->u.luks1.hdr, vk, cd->pbkdf.time_ms,
&cd->u.luks1.PBKDF2_per_sec, cd);
&cd->u.luks1.hdr, vk, cd);
if (keyslot_old == keyslot_new) {
if (r >= 0)
......@@ -2157,7 +2175,7 @@ int crypt_keyslot_add_by_keyfile_offset(struct crypt_device *cd,
goto out;
r = LUKS_set_key(keyslot, new_password, new_passwordLen,
&cd->u.luks1.hdr, vk, cd->pbkdf.time_ms, &cd->u.luks1.PBKDF2_per_sec, cd);
&cd->u.luks1.hdr, vk, cd);
out:
crypt_safe_free(password);
crypt_safe_free(new_password);
......@@ -2215,7 +2233,7 @@ int crypt_keyslot_add_by_volume_key(struct crypt_device *cd,
goto out;
r = LUKS_set_key(keyslot, passphrase, passphrase_size,
&cd->u.luks1.hdr, vk, cd->pbkdf.time_ms, &cd->u.luks1.PBKDF2_per_sec, cd);
&cd->u.luks1.hdr, vk, cd);
out:
crypt_free_volume_key(vk);
return (r < 0) ? r : keyslot;
......
......@@ -255,11 +255,77 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd,
r = crypt_pbkdf_perf(pbkdf->type, pbkdf->hash, password, password_size,
salt, salt_size, volume_key_size, pbkdf->time_ms,
pbkdf->max_memory_kb, pbkdf->parallel_threads,
&pbkdf->time_ms, &pbkdf->max_memory_kb, progress, usrptr);
&pbkdf->iterations, &pbkdf->max_memory_kb, progress, usrptr);
if (!r)
log_dbg("Benchmark returns %s(%s) %u iterations, %u memory, %u threads (for %zu-bits key).",
pbkdf->type, kdf_opt, pbkdf->time_ms, pbkdf->max_memory_kb,
pbkdf->type, kdf_opt, pbkdf->iterations, pbkdf->max_memory_kb,
pbkdf->parallel_threads, volume_key_size * 8);
return r;
}
static int benchmark_callback(long time_ms, void *usrptr)
{
struct crypt_pbkdf_type *pbkdf = usrptr;
log_dbg("PBKDF benchmark: memory cost = %u, iterations = %u, "
"threads = %u (took %ld ms)", pbkdf->max_memory_kb,
pbkdf->iterations, pbkdf->parallel_threads, time_ms);
return 0;
}
/*
* Used in internal places to benchmark crypt_device context PBKDF.
* Once requested parameters are benchmarked, iterations attribute is set,
* and the benchamarked values can be reused.
* Note that memory cost can be changed after benchark (if used).
* NOTE: You need to check that you are benchmarking for the same key size.
*/
int crypt_benchmark_pbkdf_internal(struct crypt_device *cd,
struct crypt_pbkdf_type *pbkdf,
size_t volume_key_size)
{
double PBKDF2_tmp;
uint32_t ms_tmp;
int r = -EINVAL;
/* Already benchmarked */
if (pbkdf->iterations) {
log_dbg("Reusing PBKDF benchmark values.");
return 0;
}
if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
/*
* For PBKDF2 it is enouch to run benchmark for only 1 second
* and interpolate final iterarions value from it.
*/
ms_tmp = pbkdf->time_ms;
pbkdf->time_ms = 1000;
pbkdf->parallel_threads = 0; /* N/A in PBKDF2 */
pbkdf->max_memory_kb = 0; /* N/A in PBKDF2 */
r = crypt_benchmark_pbkdf(cd, pbkdf, "foo", 3, "bar", 3,
volume_key_size, &benchmark_callback, pbkdf);
pbkdf->time_ms = ms_tmp;
if (r < 0) {
log_err(cd, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"),
pbkdf->hash);
return r;
}
PBKDF2_tmp = ((double)pbkdf->iterations * pbkdf->time_ms / 1000.);
if (PBKDF2_tmp > (double)UINT32_MAX)
return -EINVAL;
pbkdf->iterations = at_least((uint32_t)PBKDF2_tmp, MIN_PBKDF2_ITERATIONS);
} else {
r = crypt_benchmark_pbkdf(cd, pbkdf, "foo", 3,
"0123456789abcdef0123456789abcdef", 32,
volume_key_size, &benchmark_callback, pbkdf);
if (r < 0)
log_err(cd, _("Not compatible PBKDF options.\n"));
}
return r;
}
......@@ -543,7 +543,7 @@ static int benchmark_callback(long time_ms, void *usrptr)
else
log_dbg("PBKDF benchmark: memory cost = %u, iterations = %u, "
"threads = %u (took %ld ms)", pbkdf->max_memory_kb,
pbkdf->time_ms, pbkdf->parallel_threads, time_ms);
pbkdf->iterations, pbkdf->parallel_threads, time_ms);
return r;
}
......@@ -563,7 +563,7 @@ static int action_benchmark_kdf(const char *kdf, const char *hash, size_t key_si
log_std("PBKDF2-%-9s N/A\n", hash);
else
log_std("PBKDF2-%-9s %7u iterations per second for %zu-bit key\n",
hash, pbkdf.time_ms, key_size * 8);
hash, pbkdf.iterations, key_size * 8);
} else {
struct crypt_pbkdf_type pbkdf = {
.type = kdf,
......@@ -573,16 +573,16 @@ static int action_benchmark_kdf(const char *kdf, const char *hash, size_t key_si
};
r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3,
"barbarbarbarbarbar", 18, key_size,
&benchmark_callback, &pbkdf);
"0123456789abcdef0123456789abcdef", 32,
key_size, &benchmark_callback, &pbkdf);
if (r < 0)
log_std("%-10s N/A\n", kdf);
else
log_std("%-10s %4u iterations, %5u memory, "
"%1u parallel threads (CPUs) for "
"%zu-bit key (requested %u ms time)\n", kdf,
pbkdf.time_ms, pbkdf.max_memory_kb, pbkdf.parallel_threads,
key_size * 8, opt_iteration_time ?: 800);
pbkdf.iterations, pbkdf.max_memory_kb, pbkdf.parallel_threads,
key_size * 8, pbkdf.time_ms);
}
return r;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment