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,
size_t volume_key_size, uint32_t time_ms,
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
/* internal PBKDF2 implementation */

View File

@@ -2,6 +2,7 @@
* PBKDF performance check
* Copyright (C) 2012-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2012-2017, Milan Broz
* Copyright (C) 2016-2017, Ondrej Mosnacek
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -26,15 +27,6 @@
#include <sys/resource.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_FAST 10
#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,
const char *salt, size_t salt_length, size_t key_length,
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;
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_atleast = (long)target_ms * BENCH_PERCENT_ATLEAST / 100;
long ms_atmost = (long)target_ms * BENCH_PERCENT_ATMOST / 100;
struct timespec tstart, tend;
if (key_length <= 0 || target_ms <= 0)
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)
return -EINVAL;
clock_gettime(CLOCK_MONOTONIC, &tstart);
key = malloc(key_length);
if (!key)
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,
key, key_length, t_cost, m_cost, parallel,
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)
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)
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.
@@ -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,
key, key_length, t_cost, m_cost, parallel,
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)
goto out;
bench_log("Candidate parameters: t_cost = %lu; m_cost = %lu; ms = %lu\n",
(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;
} while (ms < ms_atleast || ms > ms_atmost);
out:
if (key) {
crypt_backend_memzero(key, key_length);
@@ -249,7 +239,9 @@ out:
static int crypt_pbkdf_check(const char *kdf, const char *hash,
const char *password, size_t password_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;
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,
salt_length, key, key_length, iterations, 0, 0);
if (r < 0)
goto out;
@@ -282,6 +275,14 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash,
}
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)
break;
@@ -299,9 +300,6 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash,
goto out;
}
}
if (iter_secs)
*iter_secs = (iterations * 1000) / ms;
out:
if (key) {
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,
size_t volume_key_size, uint32_t time_ms,
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;
int r = -EINVAL;
if (!kdf)
if (!kdf || !iterations_out || !memory_out)
return -EINVAL;
if (!strcmp(kdf, "pbkdf2")) {
if (!iterations_out || memory_out)
return -EINVAL;
*memory_out = 0;
*iterations_out = 0;
if (!strcmp(kdf, "pbkdf2"))
r = crypt_pbkdf_check(kdf, hash, password, password_size,
salt, salt_size, volume_key_size, &iterations_sec);
*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;
salt, salt_size, volume_key_size,
iterations_out, time_ms, progress, usrptr);
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;
}

View File

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

View File

@@ -727,7 +727,7 @@ int LUKS_generate_phdr(struct luks_phdr *header,
size_t blocksPerStripeSet, currentSector;
int r;
uuid_t partitionUuid;
const struct crypt_pbkdf_type pbkdf = {
struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2,
.hash = hashSpec,
.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,
PBKDF2_per_sec, NULL);
NULL, NULL);
if (r < 0) {
log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"),
header->hashSpec);
@@ -793,6 +793,7 @@ int LUKS_generate_phdr(struct luks_phdr *header,
}
/* 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);
@@ -864,7 +865,7 @@ int LUKS_set_key(unsigned int keyIndex,
char *AfKey = NULL;
size_t AFEKSize;
double PBKDF2_temp;
const struct crypt_pbkdf_type pbkdf = {
struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2,
.hash = hdr->hashSpec,
.time_ms = 1000,
@@ -887,7 +888,7 @@ 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,
PBKDF2_per_sec, NULL);
NULL, NULL);
if (r < 0) {
log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"),
hdr->hashSpec);
@@ -897,6 +898,7 @@ int LUKS_set_key(unsigned int keyIndex,
/*
* 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,

View File

@@ -232,14 +232,14 @@ out:
}
int crypt_benchmark_pbkdf(struct crypt_device *cd,
const struct crypt_pbkdf_type *pbkdf,
struct crypt_pbkdf_type *pbkdf,
const char *password,
size_t password_size,
const char *salt,
size_t salt_size,
size_t volume_key_size,
uint32_t *iterations,
uint32_t *memory)
int (*progress)(long time_ms, void *usrptr),
void *usrptr)
{
int r;
@@ -248,7 +248,7 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd,
return r;
/* 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);
else
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,
salt, salt_size, volume_key_size, pbkdf->time_ms,
pbkdf->max_memory_kb, pbkdf->parallel_threads,
iterations, memory);
&pbkdf->time_ms, &pbkdf->max_memory_kb, progress, usrptr);
if (!r)
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);
return r;
}

View File

@@ -532,43 +532,57 @@ out:
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)
{
int r;
if (!strcmp(kdf, CRYPT_KDF_PBKDF2)) {
const struct crypt_pbkdf_type pbkdf = {
struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2,
.hash = hash,
.time_ms = 1000,
};
uint32_t kdf_iters;
r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3, "bar", 3, key_size,
&kdf_iters, NULL);
&benchmark_callback, &pbkdf);
if (r < 0)
log_std("PBKDF2-%-9s N/A\n", hash);
else
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 {
const struct crypt_pbkdf_type pbkdf = {
struct crypt_pbkdf_type pbkdf = {
.type = kdf,
.time_ms = opt_iteration_time ?: 800,
.max_memory_kb = opt_pbkdf_memory,
.parallel_threads = opt_pbkdf_parallel,
};
uint32_t iters, memory;
r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3,
"barbarbarbarbarbar", 18, key_size, &iters, &memory);
"barbarbarbarbarbar", 18, 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 (%u ms time)\n", kdf,
iters, memory, pbkdf.parallel_threads,
key_size * 8, (unsigned)pbkdf.time_ms);
"%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);
}
return r;