Commit 4125beb0 authored by Milan Broz's avatar Milan Broz

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).
parent 93ea4f4f
......@@ -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 */
......
......@@ -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 < 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);
if (!r) {
/* Update parameters to actual measurement */
*out_t_cost = t_cost;
*out_m_cost = m_cost;
if (progress && progress(ms, usrptr))
r = -EINTR;
}
bench_log("Benchmark took: %ld ms\n", timespec_ms(&tstart, &tend));
if (r < 0)
goto out;
*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;
}
......@@ -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);
/** @} */
......
......@@ -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,
......
......@@ -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;
}
......@@ -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;
......
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