Add callback for PBKDF benchmark.

Also change API so the kdf structure is continuously updated
with the benchmarked data (callback can see progress or debug).
This commit is contained in:
Milan Broz
2017-08-09 13:41:11 +02:00
parent 93ea4f4f6f
commit 4125beb0fb
6 changed files with 89 additions and 78 deletions

View File

@@ -67,7 +67,8 @@ int crypt_pbkdf_perf(const char *kdf, const char *hash,
const char *salt, size_t salt_size, const char *salt, size_t salt_size,
size_t volume_key_size, uint32_t time_ms, size_t volume_key_size, uint32_t time_ms,
uint32_t max_memory_kb, uint32_t parallel_threads, uint32_t max_memory_kb, uint32_t parallel_threads,
uint32_t *iterations_out, uint32_t *memory_out); uint32_t *iterations_out, uint32_t *memory_out,
int (*progress)(long time_ms, void *usrptr), void *usrptr);
#if USE_INTERNAL_PBKDF2 #if USE_INTERNAL_PBKDF2
/* internal PBKDF2 implementation */ /* internal PBKDF2 implementation */

View File

@@ -2,6 +2,7 @@
* PBKDF performance check * PBKDF performance check
* Copyright (C) 2012-2017, Red Hat, Inc. All rights reserved. * Copyright (C) 2012-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2012-2017, Milan Broz * Copyright (C) 2012-2017, Milan Broz
* Copyright (C) 2016-2017, Ondrej Mosnacek
* *
* This file is free software; you can redistribute it and/or * This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@@ -26,15 +27,6 @@
#include <sys/resource.h> #include <sys/resource.h>
#include "crypto_backend.h" #include "crypto_backend.h"
//#define BENCH_DEBUG
#ifdef BENCH_DEBUG
#include <stdio.h> /* FIXME: debug */
#define bench_log(args...) fprintf(stderr, args)
#else
#define bench_log(args...)
#endif
#define BENCH_MIN_MS 250 #define BENCH_MIN_MS 250
#define BENCH_MIN_MS_FAST 10 #define BENCH_MIN_MS_FAST 10
#define BENCH_PERCENT_ATLEAST 95 #define BENCH_PERCENT_ATLEAST 95
@@ -123,7 +115,8 @@ static int measure_argon2(const char *kdf, const char *password, size_t password
static int crypt_argon2_check(const char *kdf, const char *password, size_t password_length, static int crypt_argon2_check(const char *kdf, const char *password, size_t password_length,
const char *salt, size_t salt_length, size_t key_length, const char *salt, size_t salt_length, size_t key_length,
uint32_t min_t_cost, uint32_t max_m_cost, uint32_t parallel, uint32_t min_t_cost, uint32_t max_m_cost, uint32_t parallel,
int target_ms, uint32_t *out_t_cost, uint32_t *out_m_cost) int target_ms, uint32_t *out_t_cost, uint32_t *out_m_cost,
int (*progress)(long time_ms, void *usrptr), void *usrptr)
{ {
int r = 0; int r = 0;
char *key = NULL; char *key = NULL;
@@ -132,7 +125,6 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass
long ms; long ms;
long ms_atleast = (long)target_ms * BENCH_PERCENT_ATLEAST / 100; long ms_atleast = (long)target_ms * BENCH_PERCENT_ATLEAST / 100;
long ms_atmost = (long)target_ms * BENCH_PERCENT_ATMOST / 100; long ms_atmost = (long)target_ms * BENCH_PERCENT_ATMOST / 100;
struct timespec tstart, tend;
if (key_length <= 0 || target_ms <= 0) if (key_length <= 0 || target_ms <= 0)
return -EINVAL; return -EINVAL;
@@ -140,8 +132,6 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass
if (max_m_cost < min_m_cost) if (max_m_cost < min_m_cost)
return -EINVAL; return -EINVAL;
clock_gettime(CLOCK_MONOTONIC, &tstart);
key = malloc(key_length); key = malloc(key_length);
if (!key) if (!key)
return -ENOMEM; return -ENOMEM;
@@ -154,12 +144,17 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass
r = measure_argon2(kdf, password, password_length, salt, salt_length, r = measure_argon2(kdf, password, password_length, salt, salt_length,
key, key_length, t_cost, m_cost, parallel, key, key_length, t_cost, m_cost, parallel,
BENCH_SAMPLES_FAST, BENCH_MIN_MS, &ms); BENCH_SAMPLES_FAST, BENCH_MIN_MS, &ms);
if (!r) {
/* Update parameters to actual measurement */
*out_t_cost = t_cost;
*out_m_cost = m_cost;
if (progress && progress(ms, usrptr))
r = -EINTR;
}
if (r < 0) if (r < 0)
goto out; goto out;
bench_log("Pre-initial parameters: t_cost = %lu; m_cost = %lu; ms = %lu\n",
(long unsigned)t_cost, (long unsigned)m_cost, ms);
if (ms >= BENCH_MIN_MS) if (ms >= BENCH_MIN_MS)
break; break;
@@ -188,8 +183,6 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass
} }
} }
} }
bench_log("Initial parameters: t_cost = %lu; m_cost = %lu; ms = %lu\n",
(long unsigned)t_cost, (long unsigned)m_cost, ms);
/* /*
* 2. Use the params obtained in (1.) to estimate the target params. * 2. Use the params obtained in (1.) to estimate the target params.
@@ -221,22 +214,19 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass
r = measure_argon2(kdf, password, password_length, salt, salt_length, r = measure_argon2(kdf, password, password_length, salt, salt_length,
key, key_length, t_cost, m_cost, parallel, key, key_length, t_cost, m_cost, parallel,
BENCH_SAMPLES_SLOW, ms_atleast, &ms); BENCH_SAMPLES_SLOW, ms_atleast, &ms);
if (!r) {
/* Update parameters to actual measurement */
*out_t_cost = t_cost;
*out_m_cost = m_cost;
if (progress && progress(ms, usrptr))
r = -EINTR;
}
if (r < 0) if (r < 0)
goto out; goto out;
bench_log("Candidate parameters: t_cost = %lu; m_cost = %lu; ms = %lu\n", } while (ms < ms_atleast || ms > ms_atmost);
(long unsigned)t_cost, (long unsigned)m_cost, ms);
} while(ms < ms_atleast || ms > ms_atmost);
bench_log("Accepted parameters: t_cost = %lu; m_cost = %lu\n",
(long unsigned)t_cost, (long unsigned)m_cost);
clock_gettime(CLOCK_MONOTONIC, &tend);
bench_log("Benchmark took: %ld ms\n", timespec_ms(&tstart, &tend));
*out_t_cost = t_cost;
*out_m_cost = m_cost;
out: out:
if (key) { if (key) {
crypt_backend_memzero(key, key_length); crypt_backend_memzero(key, key_length);
@@ -249,7 +239,9 @@ out:
static int crypt_pbkdf_check(const char *kdf, const char *hash, static int crypt_pbkdf_check(const char *kdf, const char *hash,
const char *password, size_t password_length, const char *password, size_t password_length,
const char *salt, size_t salt_length, const char *salt, size_t salt_length,
size_t key_length, uint32_t *iter_secs) size_t key_length, uint32_t *iter_secs, int target_ms,
int (*progress)(long time_ms, void *usrptr), void *usrptr)
{ {
struct rusage rstart, rend; struct rusage rstart, rend;
int r = 0, step = 0; int r = 0, step = 0;
@@ -273,6 +265,7 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash,
r = crypt_pbkdf(kdf, hash, password, password_length, salt, r = crypt_pbkdf(kdf, hash, password, password_length, salt,
salt_length, key, key_length, iterations, 0, 0); salt_length, key, key_length, iterations, 0, 0);
if (r < 0) if (r < 0)
goto out; goto out;
@@ -282,6 +275,14 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash,
} }
ms = time_ms(&rstart, &rend); ms = time_ms(&rstart, &rend);
*iter_secs = (uint32_t)((uint64_t)iterations * (uint64_t)target_ms / (uint64_t)ms);
if (progress && progress(ms, usrptr)) {
r = -EINTR;
goto out;
}
if (ms > 500) if (ms > 500)
break; break;
@@ -299,9 +300,6 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash,
goto out; goto out;
} }
} }
if (iter_secs)
*iter_secs = (iterations * 1000) / ms;
out: out:
if (key) { if (key) {
crypt_backend_memzero(key, key_length); crypt_backend_memzero(key, key_length);
@@ -317,31 +315,27 @@ int crypt_pbkdf_perf(const char *kdf, const char *hash,
const char *salt, size_t salt_size, const char *salt, size_t salt_size,
size_t volume_key_size, uint32_t time_ms, size_t volume_key_size, uint32_t time_ms,
uint32_t max_memory_kb, uint32_t parallel_threads, uint32_t max_memory_kb, uint32_t parallel_threads,
uint32_t *iterations_out, uint32_t *memory_out) uint32_t *iterations_out, uint32_t *memory_out,
int (*progress)(long time_ms, void *usrptr), void *usrptr)
{ {
uint32_t iterations_sec; int r = -EINVAL;
int r;
if (!kdf) if (!kdf || !iterations_out || !memory_out)
return -EINVAL; return -EINVAL;
if (!strcmp(kdf, "pbkdf2")) { *memory_out = 0;
if (!iterations_out || memory_out) *iterations_out = 0;
return -EINVAL;
if (!strcmp(kdf, "pbkdf2"))
r = crypt_pbkdf_check(kdf, hash, password, password_size, r = crypt_pbkdf_check(kdf, hash, password, password_size,
salt, salt_size, volume_key_size, &iterations_sec); salt, salt_size, volume_key_size,
iterations_out, time_ms, progress, usrptr);
*iterations_out = (uint32_t)((uint64_t)iterations_sec * (uint64_t)time_ms / 1000);
} else if (!strncmp(kdf, "argon2", 6)) {
if (!iterations_out || !memory_out)
return -EINVAL;
r = crypt_argon2_check(kdf, password, password_size, salt, salt_size,
volume_key_size, ARGON2_MIN_T_COST, max_memory_kb,
parallel_threads, time_ms, iterations_out, memory_out);
} else
r = -EINVAL;
else if (!strncmp(kdf, "argon2", 6))
r = crypt_argon2_check(kdf, password, password_size,
salt, salt_size, volume_key_size,
ARGON2_MIN_T_COST, max_memory_kb,
parallel_threads, time_ms, iterations_out,
memory_out, progress, usrptr);
return r; return r;
} }

View File

@@ -1110,14 +1110,14 @@ int crypt_benchmark(struct crypt_device *cd,
* @return @e 0 on success or negative errno value otherwise. * @return @e 0 on success or negative errno value otherwise.
*/ */
int crypt_benchmark_pbkdf(struct crypt_device *cd, int crypt_benchmark_pbkdf(struct crypt_device *cd,
const struct crypt_pbkdf_type *pbkdf, struct crypt_pbkdf_type *pbkdf,
const char *password, const char *password,
size_t password_size, size_t password_size,
const char *salt, const char *salt,
size_t salt_size, size_t salt_size,
size_t volume_key_size, size_t volume_key_size,
uint32_t *iterations, int (*progress)(long time_ms, void *usrptr),
uint32_t *memory); void *usrptr);
/** @} */ /** @} */

View File

@@ -727,7 +727,7 @@ int LUKS_generate_phdr(struct luks_phdr *header,
size_t blocksPerStripeSet, currentSector; size_t blocksPerStripeSet, currentSector;
int r; int r;
uuid_t partitionUuid; uuid_t partitionUuid;
const struct crypt_pbkdf_type pbkdf = { struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2, .type = CRYPT_KDF_PBKDF2,
.hash = hashSpec, .hash = hashSpec,
.time_ms = 1000, .time_ms = 1000,
@@ -785,7 +785,7 @@ int LUKS_generate_phdr(struct luks_phdr *header,
} }
r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength, r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength,
PBKDF2_per_sec, NULL); NULL, NULL);
if (r < 0) { if (r < 0) {
log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"), log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"),
header->hashSpec); header->hashSpec);
@@ -793,6 +793,7 @@ int LUKS_generate_phdr(struct luks_phdr *header,
} }
/* Compute master key digest */ /* Compute master key digest */
*PBKDF2_per_sec = pbkdf.time_ms;
iteration_time_ms /= 8; iteration_time_ms /= 8;
header->mkDigestIterations = at_least((uint32_t)(*PBKDF2_per_sec/1024) * iteration_time_ms, header->mkDigestIterations = at_least((uint32_t)(*PBKDF2_per_sec/1024) * iteration_time_ms,
LUKS_MKD_ITERATIONS_MIN); LUKS_MKD_ITERATIONS_MIN);
@@ -864,7 +865,7 @@ int LUKS_set_key(unsigned int keyIndex,
char *AfKey = NULL; char *AfKey = NULL;
size_t AFEKSize; size_t AFEKSize;
double PBKDF2_temp; double PBKDF2_temp;
const struct crypt_pbkdf_type pbkdf = { struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2, .type = CRYPT_KDF_PBKDF2,
.hash = hdr->hashSpec, .hash = hdr->hashSpec,
.time_ms = 1000, .time_ms = 1000,
@@ -887,7 +888,7 @@ int LUKS_set_key(unsigned int keyIndex,
log_dbg("Calculating data for key slot %d", keyIndex); log_dbg("Calculating data for key slot %d", keyIndex);
r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength, r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength,
PBKDF2_per_sec, NULL); NULL, NULL);
if (r < 0) { if (r < 0) {
log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"), log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"),
hdr->hashSpec); hdr->hashSpec);
@@ -897,6 +898,7 @@ int LUKS_set_key(unsigned int keyIndex,
/* /*
* Final iteration count is at least LUKS_SLOT_ITERATIONS_MIN * 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.); PBKDF2_temp = ((double)*PBKDF2_per_sec * iteration_time_ms / 1000.);
assert(PBKDF2_temp < UINT32_MAX); assert(PBKDF2_temp < UINT32_MAX);
hdr->keyblock[keyIndex].passwordIterations = at_least((uint32_t)PBKDF2_temp, hdr->keyblock[keyIndex].passwordIterations = at_least((uint32_t)PBKDF2_temp,

View File

@@ -232,14 +232,14 @@ out:
} }
int crypt_benchmark_pbkdf(struct crypt_device *cd, int crypt_benchmark_pbkdf(struct crypt_device *cd,
const struct crypt_pbkdf_type *pbkdf, struct crypt_pbkdf_type *pbkdf,
const char *password, const char *password,
size_t password_size, size_t password_size,
const char *salt, const char *salt,
size_t salt_size, size_t salt_size,
size_t volume_key_size, size_t volume_key_size,
uint32_t *iterations, int (*progress)(long time_ms, void *usrptr),
uint32_t *memory) void *usrptr)
{ {
int r; int r;
@@ -248,7 +248,7 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd,
return r; return r;
/* Hack to not print hash for argon, it is used also for AF later.*/ /* Hack to not print hash for argon, it is used also for AF later.*/
if (pbkdf->hash && !memory) if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2))
log_dbg("Running %s-%s benchmark.", pbkdf->type, pbkdf->hash); log_dbg("Running %s-%s benchmark.", pbkdf->type, pbkdf->hash);
else else
log_dbg("Running %s benchmark.", pbkdf->type); log_dbg("Running %s benchmark.", pbkdf->type);
@@ -256,11 +256,11 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd,
r = crypt_pbkdf_perf(pbkdf->type, pbkdf->hash, password, password_size, r = crypt_pbkdf_perf(pbkdf->type, pbkdf->hash, password, password_size,
salt, salt_size, volume_key_size, pbkdf->time_ms, salt, salt_size, volume_key_size, pbkdf->time_ms,
pbkdf->max_memory_kb, pbkdf->parallel_threads, pbkdf->max_memory_kb, pbkdf->parallel_threads,
iterations, memory); &pbkdf->time_ms, &pbkdf->max_memory_kb, progress, usrptr);
if (!r) if (!r)
log_dbg(" %u iterations, %u memory (for %zu-bits key).", log_dbg(" %u iterations, %u memory (for %zu-bits key).",
iterations ? *iterations : 0, memory ? *memory : 0, pbkdf->time_ms, pbkdf->max_memory_kb,
volume_key_size * 8); volume_key_size * 8);
return r; return r;
} }

View File

@@ -532,43 +532,57 @@ out:
return r; return r;
} }
static int benchmark_callback(long time_ms, void *usrptr)
{
struct crypt_pbkdf_type *pbkdf = usrptr;
int r = 0;
check_signal(&r);
if (r)
log_err("Benchmark interrupted.\n");
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);
return r;
}
static int action_benchmark_kdf(const char *kdf, const char *hash, size_t key_size) static int action_benchmark_kdf(const char *kdf, const char *hash, size_t key_size)
{ {
int r; int r;
if (!strcmp(kdf, CRYPT_KDF_PBKDF2)) { if (!strcmp(kdf, CRYPT_KDF_PBKDF2)) {
const struct crypt_pbkdf_type pbkdf = { struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2, .type = CRYPT_KDF_PBKDF2,
.hash = hash, .hash = hash,
.time_ms = 1000, .time_ms = 1000,
}; };
uint32_t kdf_iters;
r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3, "bar", 3, key_size, r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3, "bar", 3, key_size,
&kdf_iters, NULL); &benchmark_callback, &pbkdf);
if (r < 0) if (r < 0)
log_std("PBKDF2-%-9s N/A\n", hash); log_std("PBKDF2-%-9s N/A\n", hash);
else else
log_std("PBKDF2-%-9s %7u iterations per second for %zu-bit key\n", log_std("PBKDF2-%-9s %7u iterations per second for %zu-bit key\n",
hash, kdf_iters, key_size * 8); hash, pbkdf.time_ms, key_size * 8);
} else { } else {
const struct crypt_pbkdf_type pbkdf = { struct crypt_pbkdf_type pbkdf = {
.type = kdf, .type = kdf,
.time_ms = opt_iteration_time ?: 800, .time_ms = opt_iteration_time ?: 800,
.max_memory_kb = opt_pbkdf_memory, .max_memory_kb = opt_pbkdf_memory,
.parallel_threads = opt_pbkdf_parallel, .parallel_threads = opt_pbkdf_parallel,
}; };
uint32_t iters, memory;
r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3, r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3,
"barbarbarbarbarbar", 18, key_size, &iters, &memory); "barbarbarbarbarbar", 18, key_size,
&benchmark_callback, &pbkdf);
if (r < 0) if (r < 0)
log_std("%-10s N/A\n", kdf); log_std("%-10s N/A\n", kdf);
else else
log_std("%-10s %4u iterations, %5u memory, " log_std("%-10s %4u iterations, %5u memory, "
"%1u parallel threads (CPUs) for " "%1u parallel threads (CPUs) for "
"%zu-bit key (%u ms time)\n", kdf, "%zu-bit key (requested %u ms time)\n", kdf,
iters, memory, pbkdf.parallel_threads, pbkdf.time_ms, pbkdf.max_memory_kb, pbkdf.parallel_threads,
key_size * 8, (unsigned)pbkdf.time_ms); key_size * 8, opt_iteration_time ?: 800);
} }
return r; return r;